幕后的.NET's ThreadPool类
在rotor的\clr\src\vm\下面,有win32threadpool.h和win32threadpool.cpp两个文件,其头部写着“This module implements Threadpool support using Win32 APIs”,可见池子是用win32 API来实现的。
在h文件的头部可以看到一些声明:
static BOOL SetMaxThreads(DWORD MaxWorkerThreads,
DWORD MaxIOCompletionThreads);
static BOOL GetMaxThreads(DWORD* MaxWorkerThreads,
DWORD* MaxIOCompletionThreads);
static BOOL SetMinThreads(DWORD MinWorkerThreads,
DWORD MinIOCompletionThreads);
static BOOL GetMinThreads(DWORD* MinWorkerThreads,
DWORD* MinIOCompletionThreads);
static BOOL GetAvailableThreads(DWORD* AvailableWorkerThreads,
DWORD* AvailableIOCompletionThreads);
static BOOL QueueUserWorkItem(LPTHREAD_START_ROUTINE Function,
PVOID Context,
ULONG Flags);
是不是很眼熟?对,这些就是对应着.NET里相应的函数。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
此块里的文字和图片来自.NET's ThreadPool Class - Behind The Scenes,其中的测试代码大家可以下载试试,很有好处。
作者:Marc Clifton
我本想自己总结来着,但是这老哥总结的很好,借用了。
QueueUserWorkItem
这个方法将工作线程的委托放进一个队列,然后测试是否应该创建一个新的线程。如果需要的话,则会调用CreateWorkerThread
方法并且退出。相反的,如果这时候不创建,则另外一个不同的线程,如果不存在的话则会调用CreateThreadGate创建。
ShouldGrowWorkerThread
方法测试三个参数来决定是否应该创建一个新线程。
值得注意的是这个函数首先测试运行的线程数量是否小于可用的CPU数量。很明显这个方法将会返回false当一个或多个运行线程在一个单核机器上运行时。当这个时候,thread gate将被用来稍后创建一个线程。
CreateWorkerThread
方法是用来实例化一个实际的工作线程。
工作线程将会循环等待直至发生WorkRequestNotification事件,40s之后超时如果事件没有通知。假设事件通知了,会连续将委托从队列里删除(将事件置为未通知状态),检验是否包含的是一个合法的委托,然后调用委托。当委托返回时,工作线程立马检查是否有额外的请求加入队列。如果有,他会处理这些请求;如果没有则返回等待状态。
上述代码暗示:尽快的做你的工作。你的线程所用的任何事件都会导致队列中处理其他请求的延迟。
GateThreadStart
首次方法里会睡眠0.5s。然后他们会去尝试确定是否有一些请求在队列里。如果没有,则回去睡眠。如果有,则调用一个方法去决定时候一个新的线程被创建。此方法创建线程的延迟取决于最后线程创建的时间和当前运行线程的数量。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
关于是否有两个线程池的问题?
在CreateWorkerThread和CreateCompletionPortThread两个方法里,都调用了CreateUnimpersonatedThread这个方法来创建新线程。
其最终都是用的WIN32 API的CreateThread来创建线程,所以两种线程的来源是一样的;是不是能算的上两个线程池,逻辑上可以算做是两个吧,可能是因为为了分别响应工作线程和IO完成端口线程,所以多了区分。如果真是两个池的话,为什么不将两种线程用两套API分开呢,还非封装在一个类里?
而且工作线程和IO完成端口线程有个区别就是:后者需要和IO端口进行绑定,并且一直等待至IO请求执行返回。(此点我也了解的不是甚清楚,欢迎了解的同学补充)。
不早了,洗洗睡。