在 《利用ACL庫開發高併發半駐留式線程池程序》中介紹瞭如何使用 C 版本的 acl 線程庫編寫多線程程序,本文將會介紹如何使用 C++ 版本的 acl 線程庫編寫多線程程序,雖然 C++ 版 acl 線程庫基於 C 版的線程庫,但卻提供了更爲清晰簡潔的接口定義(很多地方參考了 JAVA 的線程接口定義)。下面是一個簡單的使用線程的例子:
#include "acl_cpp/lib_acl.hpp"
//////////////////////////////////////////////////////////////////////////
// 子線程類定義
class mythread : public acl::thread
{
public:
mythread() {}
~mythread() {}
protected:
// 基類純虛函數,當在主線程中調用線程實例的 start 函數時
// 該虛函數將會被調用
virtual void* run()
{
const char* myname = "run";
printf("%s: thread id: %lu, %lu\r\n",
myname, thread_id(), acl::thread::thread_self());
return NULL;
}
};
//////////////////////////////////////////////////////////////////////////
static void test_thread(void)
{
const char* myname = "test_thread";
mythread thr; // 子線程對象實例
// 設置線程的屬性爲非分離方式,以便於下面可以調用 wait
// 等待線程結束
thr.set_detachable(false);
// 啓動一個子線程
if (thr.start() == false)
{
printf("start thread failed\r\n");
return;
}
printf("%s: thread id is %lu, main thread id: %lu\r\n",
myname, thr.thread_id(), acl::thread::thread_self());
// 等待子線程運行結束
if (thr.wait(NULL) == false)
printf("wait thread failed\r\n");
else
printf("wait thread ok\r\n");
}
int main(void)
{
// 初始化 acl 庫
acl::acl_cpp_init();
test_thread();
#ifdef WIN32
printf("enter any key to exit ...\r\n");
getchar();
#endif
return 0;
}
從上面的示例來看,使用 acl 的線程庫創建使用線程還是非常簡單的。打開 lib_acl_cpp/include/acl_cpp/stdlib/thread.hpp 文件,可以看到線程類的聲明,其中有兩個基類:acl::thread 與 acl::thread_job,在 基類 acl::thread_job 中有一個純虛函數 run(),acl::thread 也繼承自 acl::thread_job,用戶的線程類需要繼承 acl::thread,並且需要實現基類 acl::thread_job 的純虛函數: run()。當應用在主線程中調用線程實例的 start() 函數時,acl 線程庫內部便創建一個子線程,子線程被創建後線程對象的 run() 函數便被調用。下面是 acl::thread 類中幾個主要的方法定義:
class thread_job
{
public:
thread_job() {}
virtual ~thread_job() {}
/**
* 純虛函數,子類必須實現此函數,該函數在子線程中執行
* @return {void*} 線程退出前返回的參數
*/
virtual void* run() = 0;
};
class thread : public thread_job
{
public:
thread();
virtual ~thread();
/**
* 開始啓動線程過程,一旦該函數被調用,則會立即啓動一個新的
* 子線程,在子線程中執行基類 thread_job::run 過程
* @return {bool} 是否成功創建線程
*/
bool start();
/**
* 當創建線程時爲非 detachable 狀態,則可以調用此函數
* 等待線程結束;否則,若創建線程時爲 detachable 狀態
* 在調用本函數時將會報錯
* @param out {void**} 當該參數非空指針時,該參數用來存放
* 線程退出前返回的參數
* @return {bool} 是否成功
*/
bool wait(void** out = NULL);
/**
* 在調用 start 前調用此函數可以設置所創建線程是否爲
* 分離 (detachable) 狀態;如果未調用此函數,則所創建
* 的線程默認爲分離狀態
* @param yes {bool} 是否爲分離狀態
* @return {thread&}
*/
thread& set_detachable(bool yes);
/**
* 在調用 start 前調用此函數可以設置所創建線程的堆棧大小
* @param size {size_t} 線程堆棧大小,當該值爲 0 或未
* 調用此函數,則所創建的線程堆棧大小爲系統的默認值
* @return {thread&}
*/
thread& set_stacksize(size_t size);
/**
* 在調用 start 後調用此函數可以獲得所創建線程的 id 號
* @return {unsigned long}
*/
unsigned long thread_id() const;
/**
* 當前調用者所在線程的線程 id 號
* @return {unsigned long}
*/
static unsigned long thread_self();
....
};
從上面的線程示例及 acl::thread 的類定義,也許有人會覺得應該把 acl::thread_job 的純虛方法:run() 放在 acl::thread 類中,甚至覺得 acl::thread_job 類是多餘的,但是因爲 acl 庫中還支持線程池方式,則 acl::thread_job 就顯得很有必要了。在 lib_acl_cpp\include\acl_cpp\stdlib\thread_pool.hpp 頭文件中可以看到 acl 的線程池類 acl::thread_pool 的聲明,該類的主要函數接口如下:
class thread_pool
{
/**
* 啓動線程池,在創建線程池對象後,必須首先調用此函數以啓動線程池
*/
void start();
/**
* 停止並銷燬線程池,並釋放線程池資源,調用此函數可以使所有子線程退出,
* 但並不釋放本實例,如果該類實例是動態分配的則用戶應該自釋放類實例,
* 在調用本函數後,如果想重啓線程池過程,則必須重新調用 start 過程
*/
void stop();
/**
* 等待線程池中的所有線程池執行完所有任務
*/
void wait();
/**
* 將一個任務交給線程池中的一個線程去執行,線程池中的
* 線程會執行該任務中的 run 函數
* @param job {thread_job*} 線程任務
* @return {bool} 是否成功
*/
bool run(thread_job* job);
/**
* 將一個任務交給線程池中的一個線程去執行,線程池中的
* 線程會執行該任務中的 run 函數;該函數功能與 run 功能完全相同,只是爲了
* 使 JAVA 程序員看起來更爲熟悉才提供了此接口
* @param job {thread_job*} 線程任務
* @return {bool} 是否成功
*/
bool execute(thread_job* job);
/**
* 在調用 start 前調用此函數可以設置所創建線程的堆棧大小
* @param size {size_t} 線程堆棧大小,當該值爲 0 或未
* 調用此函數,則所創建的線程堆棧大小爲系統的默認值
* @return {thread&}
*/
thread_pool& set_stacksize(size_t size);
/**
* 設置線程池最大線程個數限制
* @param max {size_t} 最大線程數,如果不調用此函數,則內部缺省值爲 100
* @return {thread_pool&}
*/
thread_pool& set_limit(size_t max);
/**
* 設置線程池中空閒線程的超時退出時間
* @param ttl {int} 空閒超時時間(秒),如果不調用此函數,則內部缺省爲 0
* @return {thread_pool&}
*/
thread_pool& set_idle(int ttl);
......
};
這些接口定義也相對簡單,下面給出一個使用線程池的例子:
// 線程工作類
class myjob : public acl::thread_job
{
public:
myjob() {}
~myjob() {}
protected:
// 基類中的純虛函數
virtual void* run()
{
const char* myname = "run";
printf("%s: thread id: %lu\r\n",
myname, acl::thread::thread_self());
return NULL;
}
};
//////////////////////////////////////////////////////////////////////////
// 線程池類
class mythread_pool : public acl::thread_pool
{
public:
mythread_pool() {}
~mythread_pool()
{
printf("thread pool destroy now, tid: %lu\r\n",
acl::thread::thread_self());
}
protected:
// 基類虛函數,當子線程被創建時該虛函數將被調用
virtual bool thread_on_init()
{
const char* myname = "thread_on_init";
printf("%s: curr tid: %lu\r\n", myname,
acl::thread::thread_self());
return true;
}
// 基類虛函數,當子線程退出前該虛函數將被調用
virtual void thread_on_exit()
{
const char* myname = "thread_on_exit";
printf("%s: curr tid: %lu\r\n", myname,
acl::thread::thread_self());
}
};
//////////////////////////////////////////////////////////////////////////
void test()
{
acl::thread_pool* threads = new mythread_pool();
threads->start(); // 啓動線程池過程
acl::thread_job *job1= new myjob, *job2 = new myjob;
threads->execute(job1);
threads->execute(job2);
// 爲了保證 job1, job2動態內存被正確釋放,
// 必須調用 threads->stop 等待子線程運行結束後在
// 主線程中將其釋放
threads->stop();
delete threads;
// 在主線程中釋放動態分配的對象
delete job1;
delete job2;
}
如上例所示,在使用 acl 的C++版本線程池類庫時,必須定義一個線程工作類(繼承自 acl::thread_job)並實現基類的純虛函數:run();另外,在使用線程池時,如果想要在線程創建時初始化一些線程局部變量以及在線程退出前釋放一些線程局部變量,則可以定義 acl::thread_pool 的子類,實現基類中的 thread_on_init 和 thread_on_exit 方法,如果不需要,則可以直接使用 acl::thread_pool 類對象。
下載:http://sourceforge.net/projects/acl/
svn:svn://svn.code.sf.net/p/acl/code/trunk
github:https://github.com/acl-dev/acl
QQ 羣:242722074