Thread Pooling
You can use thread pooling to make much more efficient use of multiple threads, depending on your application. Many applications use multiple threads, but often those threads spend a great deal of time in the sleeping state waiting for an event to occur. Other threads might enter a sleeping state and be awakened only periodically to poll for a change or update status information before going to sleep again. Using thread pooling provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks rather than thread management. In fact, if you have a number of short tasks that require more than one thread, using the ThreadPool class is the easiest and best way to take advantage of multiple threads. Using a thread pool enables the system to optimize this for better throughput not only for this process but also with respect to other processes on the computer, something your application will know nothing about. Using a thread pool enables the system to optimize thread time slices taking into account all the current processes on your computer.
The .NET Framework uses thread pools for several purposes: asynchronous calls, System.Net socket connections, asynchronous I/O completion, and timers and registered wait operations, among others.
You use the thread pool by calling ThreadPool.QueueUserWorkItem from managed code (or CorQueueUserWorkItem from unmanaged code) and passing a WaitCallback delegate wrapping the method that you want to add to the queue. You can also queue work items that are related to a wait operation to the thread pool by using ThreadPool.RegisterWaitForSingleObject and passing a WaitHandle that, when signaled or when timed out, raises a call to the method wrapped by the WaitOrTimerCallback delegate. In both cases, the thread pool uses or creates a background thread to invoke the callback method.
You can also use the unsafe methods ThreadPool.UnsafeQueueUserWorkItem and ThreadPool.UnsafeRegisterWaitForSingleObject when you know that the caller's stack is irrelevant to any security checks performed during the execution of the queued task. QueueUserWorkItem and RegisterWaitForSingleObject both capture the caller's stack, which is merged into the stack of the thread pool thread when the thread pool thread starts to execute a task. If a security check is required, that entire stack must be checked. Although the check provides safety, it also has a performance cost. Using the Unsafe method calls does not provide complete safety, but it will provide better performance.
There is only one ThreadPool object per process. The thread pool is created the first time you call ThreadPool.QueueUserWorkItem, or when a timer or registered wait operation queues a callback method. One thread monitors all tasks that have been queued to the thread pool. When a task has completed, a thread from the thread pool executes the corresponding callback method. There is no way to cancel a work item after it has been queued.
The number of operations that can be queued to the thread pool is limited only by available memory; however, the thread pool will enforce a limit on the number of threads it allows to be active in the process simultaneously (which is subject to the number of CPUs and other considerations). Each thread uses the default stack size, runs at the default priority, and is in the multithreaded apartment. If one of the threads becomes idle (as when waiting on an event) in managed code, the thread pool injects another worker thread to keep all the processors busy. If all thread pool threads are constantly busy, but there is pending work in the queue, the thread pool will, after some period of time, create another worker thread. However, the number of threads will never exceed the maximum value. The ThreadPool also switches to the correct AppDomain when executing ThreadPool callbacks.
There are several scenarios in which it is appropriate to create and manage your own threads instead of using the ThreadPool. You should do so:
- If you require a task to have a particular priority.
- If you have a task that might run a long time (and therefore block other tasks).
- If you need to place threads into a single-threaded apartment (all ThreadPool threads are in the multithreaded apartment).
- If you need to have a stable identity associated with the thread. For example, you might want to use a dedicated thread to abort that thread, suspend it, or discover it by name.
中文大致意思:线程池
应用程序 可以有多个线程,这些线程在 休眠状态 中需要耗费大量时间来等待事件发生。其他线程可能进入睡眠状态,并且仅定期被唤醒以轮循更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每个进程提供了一个线程池,一个线程池有若干个等待操作状态,当一个等待操作完成时,线程池中的 辅助线程 会执行 回调函数 。线程池中的线程由 系统管理 ,程序员不需要费力于线程管理,可以集中精力处理应用程序任务。 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是 后台 线程.每个线程都使用默认的 堆栈 大小,以默认的优先级运行,并处于多线程单元中.如果某个线程在 托管代码 中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙.如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值.超过最大值的线程可以排队,但他们要等到其他线程完成后才启动什么情况下不要使用线程池
●如果需要使一个任务具有特定优先级 ●如果具有可能会长时间运行(并因此阻塞其他任务)的任务 ●如果需要将线程放置到 单线程 单元中(线程池中的线程均处于多线程单元中) ●如果需要永久标识来标识和控制线程,比如想使用专用线程来终止该线程,将其挂起或按名称发现它 System.ThreadingPool类实现了线程池,这是一个静态类,它提供了管理线程的一系列方法 Threading.QueueUserItem方法在线程池中创建一个线程池线程来执行指定方法(用委托WaitCallBack表示),并将该线程排入线程池的队列等待执行。 public static Boolean QueueUserWorkItem(WaitCallback wc,Object state);#include <Windows.h>
#define InitializeListHead(ListHead) \
((ListHead)->Flink = (ListHead)->Blink = (ListHead) )
#define IsListEmpty(ListHead) \
(( ((ListHead)->Flink == (ListHead)) ? TRUE : FALSE ) )
#define RemoveHeadList(ListHead) \
(ListHead)->Flink;\
{\
PLIST_ENTRY FirstEntry;\
FirstEntry = (ListHead)->Flink;\
FirstEntry->Flink->Blink = (ListHead);\
(ListHead)->Flink = FirstEntry->Flink;\
}
#define RemoveEntryList(Entry) do {\
PLIST_ENTRY _EX_Entry;\
_EX_Entry = (Entry);\
_EX_Entry->Blink->Flink = _EX_Entry->Flink;\
_EX_Entry->Flink->Blink = _EX_Entry->Blink;\
} while(0);
_inline
PLIST_ENTRY
RemoveTailList(PLIST_ENTRY ListHead)
{
PLIST_ENTRY _Tail_Entry;
_Tail_Entry = ListHead->Blink;
RemoveEntryList(_Tail_Entry);
return _Tail_Entry;
}
#define InsertTailList(_ListHead,_Entry) do {\
PLIST_ENTRY _EX_ListHead = _ListHead; \
PLIST_ENTRY _EX_Blink = _EX_ListHead->Blink; \
(_Entry)->Flink = _EX_ListHead; \
(_Entry)->Blink = _EX_Blink; \
_EX_Blink->Flink = _Entry; \
_EX_ListHead->Blink = _Entry; \
} while(0);
#define InsertHeadList(_ListHead,_Entry) do {\
PLIST_ENTRY _EX_ListHead = _ListHead; \
PLIST_ENTRY _EX_Flink = _EX_ListHead->Flink; \
(_Entry)->Flink = _EX_Flink; \
(_Entry)->Blink = _EX_ListHead; \
_EX_Flink->Blink = _Entry; \
_EX_ListHead->Flink = _Entry; \
} while (0);
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 BeginTime;
LONG ItemCount;
HANDLE CompleteEvent;
DWORD WINAPI WorkItem(LPVOID lpParameter);
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<ThreadNum; i++)
{
pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);
}
return TRUE;
}
VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)
{
SetEvent(pThreadPool->QuitEvent);
for(int i=0; i<pThreadPool->ThreadNum; 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<20; i++)
{
PostWorkItem(&ThreadPool, UserProc1, (PVOID)bWaitMode);
}
WaitForSingleObject(CompleteEvent, INFINITE);
CloseHandle(CompleteEvent);
DestroyThreadPool(&ThreadPool);
}
int compute()
{
srand(BeginTime);
for(int i=0; i<20 *1000 * 1000; i++)
rand();
return rand();
}
DWORD WINAPI WorkItem(LPVOID lpParameter)
{
BOOL bWaitMode = (BOOL)lpParameter;
if(bWaitMode)
Sleep(1000);
else
compute();
printf("111111111111111111111111111 %d \n", GetTickCount() - BeginTime);
if(InterlockedDecrement(&ItemCount) == 0)
{
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<20; i++)
{
QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);
}
WaitForSingleObject(CompleteEvent, INFINITE);
CloseHandle(CompleteEvent);
}
int _tmain(int argc, _TCHAR* argv[])
{
// 自定义的线程池
//TestSimpleThreadPool(FALSE, 5);
// 可以修改后面的参数来查看运行的时间
// 调用windows上已有的线程池
TestWorkItem(FALSE, WT_TRANSFER_IMPERSONATION);
return 0;
}