在JeffreyRichter的《windows核心编程》里提到的windows线程池功能,他把这些功能分为四类:
以异步的方式来调用一个函数;
每隔一段时间调用一个函数;
当内核对象触发的时候调用一个函数;
当异步I/O请求完成的时候调用一个函数;
以下按这四种类型功能,结合其调用函数接口和程序例子,来详细讨论其用法。
直接以异步方式调用函数也有两种API方式。
这种方式需要两个函数配合使用:需要在线程池中获取线程来调用的函数必须以SimpleCallback函数指针的方式声明,再在主线程中使用用TrySubmitThreadpoolCallback函数设置此回调函数。TrySubmitThreadpoolCallback函数会调用PostQueuedCompletionStatus来将这个SimpleCallback函数的工作项加入到线程池队列中,如果成功的话,返回TRUE,失败返回FALSE。SimpleCallback声明的函数是否立即调用要看CPU的调度。
VOIDCALLBACK SimpleCallback(
_Inout_ PTP_CALLBACK_INSTANCE Instance,
_Inout_opt_ PVOID Context
);
BOOLWINAPI TrySubmitThreadpoolCallback(
_In_ PTP_SIMPLE_CALLBACK pfns,
_Inout_opt_ PVOID pv,
_In_opt_ PTP_CALLBACK_ENVIRON pcbe
);
我们不需要自己调用CreateThread来产生线程,系统会为我们的进程创建一个默认的线程池,并让线程池中的一个线程来调用我们的回调函数。这个线程池是windows精心设计的,其原理在《windows核心编程》里面讲述了一些,我尽量找多一些资料佐证这个说法。
下面一个简单的例子展示了隐式控制工作项的这两个函数的使用。
#include "stdafx.h"
#include
VOID CALLBACK MySimpleCallBack(PTP_CALLBACK_INSTANCE Instance,PVOID Context)
{
_tprintf(_T("This iscalling Test_1_SimpleCallBack!"));
return;
}
int main(void)
{
BOOL ret = TrySubmitThreadpoolCallback(MySimpleCallBack,NULL,NULL);
Sleep(10000);
}
以上的例子有一个明显的问题,没有对MySimpleCallBack调用的线程做同步的等待。如果主线程main已经退出,但MySimpleCallBack函数所在线程还在执行,可能造成的后果是未定义的。后面会讨论怎么解决这个问题,这里不详细展开。还应该注意,TrySubmitThreadpoolCallback不是一定可以调用成功的,在某些情况下,函数内部做线程调度的工作会遇到很多问题。
有时我们可能需要先创建多个工作项,然后逐个加入到线程池中执行,这个时候(也是通常的时候)需要使用显式控制工作项的函数。
使用CreateThreadpoolWork函数创建工作项,线程池会调用这个函数给出的回调函数参数WorkCallback函数指针,使用SubmitThreadpoolWork提交工作项,使用WaitForThreadpoolWorkCallbacks等待工作项完成的事件,最后,使用CloseThreadpoolWork关闭工作项。下面的这些函数的原型。
创建工作项,传进工作需要的回调函数:
PTP_WORKWINAPI CreateThreadpoolWork(
_In_ PTP_WORK_CALLBACK pfnwk,
_Inout_opt_ PVOID pv,
_In_opt_ PTP_CALLBACK_ENVIRON pcbe
);
供线程调用的回调函数原型:
VOIDCALLBACK WorkCallback(
_Inout_ PTP_CALLBACK_INSTANCE Instance,
_Inout_opt_ PVOID Context,
_Inout_ PTP_WORK Work
);
提交工作项,让线程池创建一个新线程去调用回调函数:
VOIDWINAPI SubmitThreadpoolWork(
_Inout_ PTP_WORK pwk
);
等待工作项的线程完成回调函数调用:
VOIDWINAPI WaitForThreadpoolWorkCallbacks(
_Inout_ PTP_WORK pwk,
_In_ BOOL fCancelPendingCallbacks
);
关闭工作项:
VOIDWINAPI CloseThreadpoolWork(
_Inout_ PTP_WORK pwk
);
下面上例子说明一个极简单的调用情景:
VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Parameter,PTP_WORK Work)
{
_tprintf(_T("This iscalling Test_1_WorkCallback!"));
return;
}
int main(void)
{
PTP_WORKpwWork = CreateThreadpoolWork(MyWorkCallback,NULL,NULL);
SubmitThreadpoolWork(pwWork);
WaitForThreadpoolWorkCallbacks(pwWork,FALSE);
CloseThreadpoolWork(pwWork);
return 0;
}
CreateThreadpoolTimer
PTP_TIMERWINAPI CreateThreadpoolTimer(
_In_ PTP_TIMER_CALLBACK pfnti,
_Inout_opt_ PVOID pv,
_In_opt_ PTP_CALLBACK_ENVIRON pcbe
);
VOIDCALLBACK TimerCallback(
_Inout_ PTP_CALLBACK_INSTANCE Instance,
_Inout_opt_ PVOID Context,
_Inout_ PTP_TIMER Timer
);
SetThreadpoolTimer
VOIDWINAPI SetThreadpoolTimer(
_Inout_ PTP_TIMER pti,
_In_opt_ PFILETIME pftDueTime,
_In_ DWORD msPeriod,
_In_opt_ DWORD msWindowLength
);
IsThreadpoolTimerSet
BOOLWINAPI IsThreadpoolTimerSet(
_Inout_ PTP_TIMER pti
);
WaitForThreadpoolTimerCallbacks
VOIDWINAPI WaitForThreadpoolTimerCallbacks(
_Inout_ PTP_TIMER pti,
_In_ BOOL fCancelPendingCallbacks
);
CloseThreadpoolTimer
VOIDWINAPI CloseThreadpoolTimer(
_Inout_ PTP_TIMER pti
);
同样以一个简单的例子说明使用情景:
VOID CALLBACK
MyTimerCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter,PTP_TIMER Timer)
{
_tprintf(_T("MyTimerCallback:timer has fired.\n"));
}
int main(void)
{
BOOL ret;
FILETIMEFileDueTime;
ULARGE_INTEGERulDueTime;
// Create a timerwith the same callback environment.
PTP_TIMERtimer = NULL;
PTP_TIMER_CALLBACKtimercallback = MyTimerCallback;
timer = CreateThreadpoolTimer(timercallback,NULL,NULL);
if (NULL == timer)
{
_tprintf(_T("CreateThreadpoolTimerfailed. LastError: %u\n"),
GetLastError());
}
// Set the timerto fire in one second.
ulDueTime.QuadPart = (ULONGLONG)-(1 * 10 * 1000 * 1000);
FileDueTime.dwHighDateTime = ulDueTime.HighPart;
FileDueTime.dwLowDateTime = ulDueTime.LowPart;
ret = IsThreadpoolTimerSet(timer);
SetThreadpoolTimer(timer,&FileDueTime,0,0);
ret = IsThreadpoolTimerSet(timer);
Sleep(5000);
WaitForThreadpoolTimerCallbacks(timer,FALSE);
CloseThreadpoolTimer(timer);
return 0;
}
这种情况是通过在线程池中创建一个等待Event发生的线程,异步完成工作的情景。
CreateThreadpoolWait
SetThreadpoolWait
WaitForThreadpoolWaitCallbacks
CloseThreadpoolWait
同样以例子说明情景:
VOID CALLBACK
MyWaitCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WAIT Wait,
TP_WAIT_RESULT WaitResult
)
{
// Do somethingwhen the wait is over.
_tprintf(_T("MyWaitCallback:wait is over.\n"));
}
int main( void)
{
PTP_WAITWait = NULL;
PTP_WAIT_CALLBACKwaitcallback = MyWaitCallback;
HANDLE hEvent = NULL;
UINT i = 0;
UINT rollback = 0;
// Create anauto-reset event.
hEvent =CreateEvent(NULL,FALSE, FALSE,NULL);
if (NULL == hEvent)
{
// Error Handling
return;
}
Wait = CreateThreadpoolWait(waitcallback,NULL,NULL);
if(NULL == Wait)
{
_tprintf(_T("CreateThreadpoolWaitfailed. LastError: %u\n"),
GetLastError());
return -1;
}
// Need tore-register the event with the wait object
// each timebefore signaling the event to trigger the wait callback.
for (i = 0; i <5; i ++)
{
SetThreadpoolWait(Wait, hEvent, NULL);
SetEvent(hEvent);
// Delay forthe waiter thread to act if necessary.
Sleep(500);
// Block hereuntil the callback function is done executing.
WaitForThreadpoolWaitCallbacks(Wait,FALSE);
}
}
CreateThreadpoolIo StartThreadpoolIo WaitForThreadpoolIoCallbacks CancelThreadpoolIo CloseThreadpoolIo |
VOIDCALLBACK IoCompletionCallback(
_Inout_ PTP_CALLBACK_INSTANCE Instance,
_Inout_opt_ PVOID Context,
_Inout_opt_ PVOID Overlapped,
_In_ ULONG IoResult,
_In_ ULONG_PTR NumberOfBytesTransferred,
_Inout_ PTP_IO Io
);
目前我无法正确使用这个系列函数,有待后续学习后修改。以下是不能调用回调函数的代码,问题还没查清楚。
VOID CALLBACK WriteIoCompletionCallback(
_Inout_ PTP_CALLBACK_INSTANCEInstance,
_Inout_opt_ PVOID Context,
_Inout_opt_ PVOID Overlapped,
_In_ ULONGIoResult,
_In_ ULONG_PTRNumberOfBytesTransferred,
_Inout_ PTP_IOIo
)
{
_tprintf(_T("WriteIoCompletionCallbackcalling"));
return ;
}
int main(void)
{
TCHAR readfilename[] = _T("d:\\test\\test.txt");
TCHAR writefilename[] = _T("d:\\test\\writetest.txt");
PTP_WIN32_IO_CALLBACKreadcallback = ReadIoCompletionCallback;
PTP_WIN32_IO_CALLBACKwritecallback = WriteIoCompletionCallback;
// 以OVERLAPPED方式打开源文件,以备读出
HANDLE hFileRead = CreateFile(readfilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED,
NULL);
// 以OVERLAPPED方式打目标文件,以备写入
HANDLE hFileWrite = CreateFile(writefilename,
GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING,NULL);
PTP_IO readThreadPool = CreateThreadpoolIo(hFileRead,readcallback,NULL,NULL);
StartThreadpoolIo(readThreadPool);
//PTP_IOwriteThreadPool = CreateThreadpoolIo(hFileWrite,writecallback,NULL,NULL);
//StartThreadpoolIo(writeThreadPool);
PVOID pvData = VirtualAlloc(NULL,1024,MEM_COMMIT,PAGE_READWRITE);
OVERLAPPEDoverlapped;
overlapped.Internal = overlapped.InternalHigh = 0;
overlapped.Offset = overlapped.OffsetHigh = 0;
::ReadFile(hFileRead,pvData,1024,NULL,&overlapped);
Sleep(30000);
WaitForThreadpoolIoCallbacks(readThreadPool,FALSE);
//WaitForThreadpoolIoCallbacks(writeThreadPool,FALSE);
return 0;
}