有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作,当等待操作完成后,线程池中将会有一个工作者线程来执行相应的回调函数。 你也可以把没有等待操作的工作项目放到线程池中,用QueueUserWorkItem函数来完成这个工作,把要执行的工作项目函数通过一个参数传递给线程池。工作项目被放到线程池中后,就不能再取消了。 Timer-queue timers和Registered wait operations也使用线程池来实现。他们的回调函数也放在线程池中。你也可以用BindIOCompletionCallback函数来投递一个异步IO操作,在IO完成端口上,回调函数也是由线程池线程来执行。 当第一次调用QueueUserWorkItem函数或者BindIOCompletionCallback函数的时候,线程池被自动创建,或者Timer-queue timers或者Registered wait operations放入回调函数的时候,线程池也可以被创建。线程池可以创建的线程数量不限,仅受限于可用的内存,每一个线程使用默认的初始堆栈大小,运行在默认的优先级上。 线程池中有两种类型的线程:IO线程和非IO线程。IO线程等待在可告警状态,工作项目作为APC放到IO线程中。如果你的工作项目需要线程执行在可警告状态,你应该将它放到IO线程。 非IO工作者线程等待在IO完成端口上,使用非IO线程比IO线程效率更高,也就是说,只要有可能的话,尽量使用非IO线程。IO线程和非IO线程在异步IO操作没有完成之前都不会退出。然而,不要在非IO线程中发出需要很长时间才能完成的异步IO请求。 正确使用线程池的方法是,工作项目函数以及它将会调用到的所有函数都必须是线程池安全的。安全的函数不应该假设线程是一次性线程的或者是永久线程。一般来说,应该避免使用线程本地存储和发出需要永久线程的异步IO调用,比如说RegNotifyChangeKeyValue函数。如果需要在永久线程中执行这样的函数的话,可以给QueueUserWorkItem传递一个选项WT_EXECUTEINPERSISTENTTHREAD。 注意,线程池不能兼容COM的单线程套间(STA)模型。 为了更深入地讲解操作系统实现的线程池的优越性,我们首先尝试着自己实现一个简单的线程池模型。 代码如下: /**//************************************************************************/ /**//* Test Our own thread pool. */ /**//************************************************************************/ typedef struct _THREAD_POOL { HANDLE QuitEvent; HANDLE WorkItemSemaphore; LONG WorkItemCount; LIST_ENTRY WorkItemHeader; CRITICAL_SECTION WorkItemLock; LONG ThreadNum; HANDLE *ThreadsArray; }THREAD_POOL, *PTHREAD_POOL; typedef VOID (*WORK_ITEM_PROC)(PVOID Param); typedef struct _WORK_ITEM { LIST_ENTRY List; WORK_ITEM_PROC UserProc; PVOID UserParam; }WORK_ITEM, *PWORK_ITEM; DWORD WINAPI WorkerThread(PVOID pParam) { PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam; HANDLE Events[2]; Events[0] = pThreadPool->QuitEvent; Events[1] = pThreadPool->WorkItemSemaphore; for(;;) { DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE); if(dwRet == WAIT_OBJECT_0) break; // // execute user's proc. // else if(dwRet == WAIT_OBJECT_0 +1) { PWORK_ITEM pWorkItem; PLIST_ENTRY pList; EnterCriticalSection(&pThreadPool->WorkItemLock); _ASSERT(!IsListEmpty(&pThreadPool->WorkItemHeader)); pList = RemoveHeadList(&pThreadPool->WorkItemHeader); LeaveCriticalSection(&pThreadPool->WorkItemLock); pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List); pWorkItem->UserProc(pWorkItem->UserParam); InterlockedDecrement(&pThreadPool->WorkItemCount); free(pWorkItem); } else { _ASSERT(0); break; } } return 0; } BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum) { pThreadPool->QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); pThreadPool->WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); pThreadPool->WorkItemCount = 0; InitializeListHead(&pThreadPool->WorkItemHeader); InitializeCriticalSection(&pThreadPool->WorkItemLock); pThreadPool->ThreadNum = ThreadNum; pThreadPool->ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum); for(int i=0; i SetEvent(pThreadPool->QuitEvent); for(int i=0; iThreadNum; i++) { WaitForSingleObject(pThreadPool->ThreadsArray[i], INFINITE); CloseHandle(pThreadPool->ThreadsArray[i]); } free(pThreadPool->ThreadsArray); CloseHandle(pThreadPool->QuitEvent); CloseHandle(pThreadPool->WorkItemSemaphore); DeleteCriticalSection(&pThreadPool->WorkItemLock); while(!IsListEmpty(&pThreadPool->WorkItemHeader)) { PWORK_ITEM pWorkItem; PLIST_ENTRY pList; pList = RemoveHeadList(&pThreadPool->WorkItemHeader); pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List); free(pWorkItem); } } BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam) { PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM)); if(pWorkItem == NULL) return FALSE; pWorkItem->UserProc = UserProc; pWorkItem->UserParam = UserParam; EnterCriticalSection(&pThreadPool->WorkItemLock); InsertTailList(&pThreadPool->WorkItemHeader, &pWorkItem->List); LeaveCriticalSection(&pThreadPool->WorkItemLock); InterlockedIncrement(&pThreadPool->WorkItemCount); ReleaseSemaphore(pThreadPool->WorkItemSemaphore, 1, NULL); return TRUE; } VOID UserProc1(PVOID dwParam) { WorkItem(dwParam); } void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum) { THREAD_POOL ThreadPool; InitializeThreadPool(&ThreadPool, ThreadNum); CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); BeginTime = GetTickCount(); ItemCount = 20; for(int i=0; i srand(BeginTime); for(int i=0; i printf("Time total %d second./n", GetTickCount() - BeginTime); SetEvent(CompleteEvent); } return 0; } void TestWorkItem(BOOL bWaitMode, DWORD Flag) { CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); BeginTime = GetTickCount(); ItemCount = 20; for(int i=0; i |