《Windows核心编程系列》谈谈Windows线程池

网络整理 - 07-27

WAIT_OBJECT_0        超时之前有对象被触发。

WAIT_TIMEOUT      由于超时导致回调函数被触发。

WAIT_ABANDONED_0   如果传入的内核对象是互斥量且被遗弃。回调函数将收到这个值。

一旦线程池调用了我们的回调函数,对应的等待项将进入不活跃状态。所谓不活跃状态:如果想让回调函数在同一个内核对象被触发时再次被调用,我们需要调用SetThreadpoolWait来再次注册。

最后我们同样可以等待一个等待项完成。这可以调用WaitForThreadpoolWaitCallbacks。还可以调用CloseThreadpoolWait来释放一个等待项的内存。

注意:不要让回调函数调用WaitForThreadpoolWork并将自己的工作项作为参数传入,这会导致死锁。

情形四:在异步IO完成时调用一个函数。

我们在上一篇博文中介绍了如何使用IO完成端口来高效的执行异步IO操作,也介绍了如何创建一个线程池并让其中的线程等待IO完成端口。这里我们将介绍线程池如何管理线程的创建和销毁。

在打开一个关联起来文件或设备时,我们必须现将该设备与线程池的IO完成端口,然后告诉线程池在异步IO完成时应该调用哪个函数。

首先我们需要定义回调函数,它需要满足一下原型:

 

VOID CALLBACK OverlappedCompletionRoutine( PTP_CALLBACK_INSTANCE pInstance, PVOID pvContext, PVOID pOverlapped, ULONG IoResult; ULONG_PTR NumberOfBytesTransferred, PTP_IO pIo);


 

当一个IO操作完成时此回调函数会被调用并得到一个指向OVERLAPPED结构的指针。此结构是我们在调用ReadFileWriteFile时传入的。

IoResult表示IO异步操作的执行结果。如果IO请求成功,将传给回调函数NO_ERROR

NumberOfBytesTransferred参数传入已传输的字节数。

pIo传入指向线程池IO项的指针。马上介绍。

pInstance后面会有介绍。

定义好回调函数后,我们就需要调用CreateThreadpoolIo来创建一个线程池IO对象。

 

PTP_IO CreateThreadpoolIo( HANDLE hDevice, PTP_WIN32_IO_CALLBACK pfnIoCallback, PVOID pvContext, PTP_CALLBACK_ENVIRON pcbe);


 

hDevice是与IO对象相关联的设备句柄。

pfnIoCallback是前面我们介绍的回调函数指针。

pvContext当然是传给回调函数的参数。

IO对象创建好之后,我们就可以通过下面的函数来将潜入在IO项的设备与IO完成端口关联起来。

 

VOID StartThreadpoolIo(PTP_IO pio);


 

关联之后我们就可以调用ReadFileWriteFile了。此后当异步IO请求完成后,回调函数将会被调用。

此外我们还可以调用以下函数来停止线程池调用回调函数,此后回调函数将不会被调用:

 

VOID CancelThreadpoolIo(PTP_IO pio);


 

CloseThreadpoolIo将取消设备与线程池的关联:

 

VOID CloseHandlepoolIo(PTP_IO pio);


 

WaitForThreadpoolIoCallbacks将等待一个待处理的IO请求我完成。

 

VOID WaitForThreadpoolIoCallback( PTP_IO pio, BOOL bCancelPendingCallbacks);


 

如果传给bCancelPendingCallbacks的值为true,那么当请求完成时,回调函数不会被调用。

           对线程池进行定制

     在调用CreateThreadpoolWorkCreateThreadpoolTimerCreateThreadpoolWaitCreateThreadpoolIo时,有一个PTP_CALLBACK_ENVIRON类型的参数。如果传给它NULL则表示我们会将工作项添加到默认的线程池中。一般情况下默认的线程池能够满足大多数情况下的要求。

     如果我们想定制我们自己的线程池,可以调用CreateThreadpool来创建新线程池:

 

PTP_POOL CreateThreadpool(PVOID reserved);


 

Reserved是保留的,传入NULL即可。

该函数返回一个PTP_POOL值,它表示新创建的线程。

    此后我们就可以设置线程池中的最大线程和最小线程了。默认的线程池中线程最少为1