伺服器程式
最核心的任務之一就是處理一組任務,在處理一組任務的時候最常見的做法是用執行緒池,最常見的執行緒池一般是由一組執行緒等待在乙個訊號燈上,有乙個任務到達後解鎖乙個執行緒,讓該執行緒去處理任務,執行緒處理完成後又回歸到執行緒池,此做法比來乙個任務分配乙個執行緒的古老方法效率高了很多,但這也不是執行緒池的唯一做法,在windows下至少有三種典型執行緒池,由於實現上的不同效率相差很大,很有必要作乙個比較,以便了解在什麼情況下用什麼執行緒池。windows下常見的執行緒
池實現有以下三種方式,分別是:基於訊號燈的傳統執行緒池(以下簡稱:傳統執行緒池),系統執行緒池,完成埠執行緒池,下面分別介紹三種典型的執行緒池。
傳統執行緒池
前面已經提到此執行緒池的典型做法是讓一組執行緒等待在同乙個訊號燈上,一旦任務佇列有新任務加入,池中某執行緒立刻被喚醒並從任務佇列取出乙個任務執行,完成後該執行緒回歸到執行緒池中。
系統執行緒池
系統執行緒池是由win2000及之後的作業系統提供的,提供了方便的應用執行緒池的功能,用在以下幾種場合:
ø 以非同步方式呼叫方法
ø 以一定的時間間隔呼叫方法
ø 當乙個核心物件得到通知時,呼叫方法
ø 當乙個非同步
i/o請求完成時,呼叫方法
完成埠執行緒池
將完成埠和一組執行緒繫結,利用完成埠的排隊和等待機制實現,由於作業系統對完成埠有專門的優化,我們期待這種模式的執行緒池有更好的表現。
上面簡單介紹了三種典型執行緒池,但什麼情況下用什麼執行緒池,我們先來測試一下效率,後面再總結。我們的測試方法是使用以上三種典型執行緒
池分別執行1000萬小任務,比較執行任務的時間,我們用來測試的
小任務是如下的乙個函式:
void singletest(void *p)
testnode *pn = static_cast(p);
if(interlockeddecrement(&pn->tasknum) == 0)
setevent(gend);
}一旦任務結束,置乙個完成事件。為了測試簡單,傳統執行緒池和完成埠模式執行緒池都用了乙個固定數目的執行緒,例子中用的是11,沒有執行動態增減執行緒之類的排程操作。
經測試結果如下表:(耗時單位為毫秒)
測試次數
傳統執行緒池耗時
系統執行緒池耗時
完成埠執行緒池耗時1
49250
72234
20391
2
50906
71266
20281
3
50156
73000
20297
4
50437
71157
20000
5
49250
73078
19547
6
49844
73156
19469
耗時比約為:5 : 7 : 2,差別巨大
從上表可以看出,我們平時最習慣使用的傳統執行緒池效率並不好,連完成埠執行緒池一半效率都不到,系統提供的執行緒池則效率更差,由此可以總結如下:
執行緒池模型
適用作業系統
效率
易用性
傳統執行緒池
任意
中
較麻煩,大多數人寫不出好的執行緒池
系統執行緒池
win2000之後
低
最簡單
完成埠執行緒池
win2000之後
高
較簡單
當然不是說系統提供的執行緒池一無是處,很多時候我們還是可以選擇使用系統執行緒池的,畢竟它的使用是最簡單的,只是需要最高效率的時候換個選擇,大多數對效率要求不是特別高的場合或你要使用timerqueue、registerwaitforsingleobject、完成埠例程等的時候必須要使用系統執行緒池,如果不考慮9x系統和nt,那麼還是不要使用自己寫的基於訊號燈方式的傳統執行緒池吧,既沒效率也不簡單。當然如果要考慮在各種平台下都通用,那麼基於訊號燈方式的傳統執行緒
池幾乎是不二之選。
至於以上三種執行緒池效率差別為什麼會這麼大,我想各位看過這個資料的都會考慮,具體原因我將另文闡述。
測試**如下:
// //
#include "stdafx.h"
#include "comparethreadpool.h"
#include "threadpool.h"
#include "threadgroup.h"
#ifdef _debug
#define new debug_new
#undef this_file
static char this_file = __file__;
#endif
///
using namespace std;
handle gend = invalid_handle_value;
handle giocp = invalid_handle_value;
/**該例子用來比較測試三種執行緒池,分別是:
我自己的執行緒池
系統提供的執行緒池
標準的完成埠模型
@author oldworm / [email protected]
2007.5.22
*/struct testnode
;void singletest(void *p)
testnode *pn = static_cast(p);
if(interlockeddecrement(&pn->tasknum) == 0)
setevent(gend);
// printf("num=%u, th=%u/r/n", pn->tasknum, getcurrentthreadid());
}void mythreadpool(void *p)
dword winapi
workitemfunc(void *param)
void thgroupfunc(void *pgroup, void *param)
}int _tmain(int argc, tchar* argv, tchar* envp)
waitforsingleobject(gend, infinite);
tn.end = gettickcount();
printf("mythreadpool方式總耗時: %u/r/n", tn.end-tn.start);
//系統執行緒池方式
tn.start = gettickcount();
tn.tasknum = tasknums;
for(i=0; i
waitforsingleobject(gend, infinite);
tn.end = gettickcount();
printf("系統執行緒
池方式總耗時: %u/r/n", tn.end-tn.start);
//完成埠形式
tn.start = gettickcount();
tn.tasknum = tasknums;
giocp = createiocompletionport(invalid_handle_value, null, 0, 0);
for(i=0; i
tg.start();
for(i=0; i
waitforsingleobject(gend, infinite);
for(i=0; i
tn.end = gettickcount();
printf("完成埠方式總耗時: %u/r/n", tn.end-tn.start);
closehandle(giocp);
closehandle(gend);
return 0;
}
三種執行緒池比較
三種執行緒池比較 伺服器程式最核心的任務之一就是處理一組任務,在處理一組任務的時候最常見的做法是用執行緒池,最常見的執行緒池一般是由一組執行緒等待在乙個訊號燈上,有乙個任務到達後解鎖乙個執行緒,讓該執行緒去處理任務,執行緒處理完成後又回歸到執行緒池,此做法比來乙個任務分配乙個執行緒的古老方法效率高了...
三種執行緒池模型
half sync half async hs ha 將執行緒分為兩個部分,一部分專門處理非同步事件,另一部分專門處理同步事件。在網路伺服器裡,一般非同步事件指的是socket事件,同步事件指的是業務邏輯。leader follower lf 所有執行緒輪流監聽請求,監聽到後將監聽令牌傳到下乙個執行...
執行緒的三種建立方式與執行緒池合用
1 繼承thread類 2 實現runnable介面 3 實現callable介面 callable介面和runnable介面類似,runnable介面重寫了run方法 callable介面重寫了call方法,前者無返回值,後者有返回值,返回值型別就是傳進來的引數值。另外callable介面還可以拋...