上一篇我把創建單個線程的源碼給出來了,當然,線程池的也不能拉下~
很多時候,我們需要一組線程來解決問題。當然可以創建一些線程來完成工作,然後關閉掉。當又需要時,重複上述過程即可。然而,無論是客戶端還是服務端,完全沒必要如此,只需要實現創建一組線程,按需分配,不必創建--關閉--創建--關閉…
也許我們也能自己寫成符合需求的線程池組件,但是,系統也提供了線程池組件。我認爲就應該重複利用(出於學習的目的另當別論)。在這裏,主要針對XP系統,提供一組C++ wrap過的線程池組件和模仿ATL線程池。當然,使用系統的線程池有一定的限制(設置線程堆棧大小等)。但還是能滿足大部分的需求。
首先,來看看系統線程池(據《Windows核心編程》所說,在內部都是用完成端口來管理的)
Windows 線程池中的線程有兩種類型,一種可以用來處理異步I/O, 另一種則不能。前者依賴於IO完成端口,IOCP是一種Windows內核對象,它可以將線程和I/O端口綁定在特定的系統資源上,對帶有完成端口的I/O進行處理是一個複雜的過程。
- QueueUserWorkItemPool--Windows將創建一個線程池,其中的一個線程將執行 回調函數,函數執行完成後,該線程返回線程池,等待新的任務。當然了,你不能在該線程的回調函數中執行ExitThread等破壞行爲。線程池中的線程數量是動態的,Windows內部的調度算法決定當前線程工作負載的最佳方式。如果要執行長時間的處理工作,需要設置WT_EXECUTELONGFUNCTION。如果該線程不執行異步IO,則設置WT_EXECUTIONDEFAULT即可,如果需要執行異步IO,則應該設置WT_EXECUTEIONIOTHREAD標誌來告訴線程池
- BindIOCompletionCallbackPool--服務器應用程序發出某些異步IO請求,當這些請求完成時,需要讓一個線程池準備好來處理已完成的IO請求。BindIoCompletionCallback在內部調用CreateIoCompletionPort,傳遞hDevice和內部完成端口的句柄。調用該函數可以保證至少有一個線程始終在非IO組件中,與設備相關的完成鍵是重疊完成例程的地址。
這樣,當該設備的IO運行完成時,非IO組件就知道調用哪個函數,以便能夠處理已完成IO請求 - RegisterWaitForSingleObjectPool--線程池在等待一個事件,當該事件被激活時,會喚醒一個線程來執行註冊的回調函數。
其次,來看如何使用,簡單展示下QueueUserWorkItemPool,其他的則在Example中給出
class A
{
enum { Count = 1000000 };
public:
void DoWork()
{
LONG lVal = 0;
for(DWORD i = 0; i != Count; ++i)
{
InterlockedExchangeAdd(&lVal, i);
}
}
void DoWork(int nNum)
{
LONG lVal = 0;
for(DWORD i = 0; i != nNum; ++i)
{
InterlockedExchangeAdd(&lVal, i);
}
}
};
A a;
QueueUserWorkItemPool::Call(&A::DoWork, &a);
QueueUserWorkItemPool::CallEx(&A::DoWork, &a, nCount);
很簡單,是吧,只需要設置回調函數,CallEx比Call多提供一個額外參數,當然可以是任意類型.
然而,在對於BindIOCompletionCallbackPool的設計時,爲了區分每個不同的回調函數,把自己弄成了模板類,需要提供一個額外的模板參數來進行區分--
BindIOCompletionCallbackPool<1>::Call和BindIOCompletionCallbackPool<2>::Call
如果你有更好的建議,希望你能提出,大家共同提高。
所有的源碼和示例以提供:(需要更改後綴,把.gif改爲.rar,用右鍵下載)
猛擊這裏 猛擊這裏