Windows线程池函数

我的梦境 提交于 2020-02-04 22:36:41

Windows提供给了一个与   完成端口   相配套的线程池机制。

1.以异步的方式调用函数                  //工作项
2.每隔一段时间调用一个函数              //计时项
3.在内核对象触发的时候调用一个函数      //等待项
4.在异步I/O请求完成的时候调用一个函数   //I/O项

1.以异步的方式调用函数

创建一个工作项,分多次提交任务。

PTP_WORK CreateThreadpoolWork(PTP_WORK_CALLBACK pfnWorkHandler,PVOID pvContext,PTP_CALLBACK_ENVIRON pcbe);

pvContext:传给回调函数的值
pcbe:和线程池的定制有关
pfnWorkHandler:函数指针,该函数的函数原型这个样子
VIOD CALLBACK WorkCallBack(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PTP_WORK Work);//Work:创建函数的返回值

向线程池提交一个任务请求:
 

VOID SubmitThreadpoolWork(PTP_WORK Work);

如果需要多次提交一个工作项,每次回调函数执行的时候,传入的Context的值肯定是相同的。

在另一个线程中,想要取消或者等待工作项处理完毕然后将线程挂起:
 

VOID WaitForThreadpoolWorkCallbacks(PTP_WORK pWork,BOOL bCancelPendingCallbacks);

pWork:指定的等待的工作项
   bCancelPendingCallbacks:TRUE:会试图取消先前提交的那个工作项,如果线程正在处理工作项,则不会打断,一直到工作项完成才返回。如果已经提交的工作项尚未被任何的线程处理,函数会把它标记为已经取消,然后立即返回。当完成端口取出该工作项的时候,线程池不会调用回调函数。FALSE:会将线程挂起,直到工作项完成并且处理该工作项的线程已经准备好处理下一个工作项位为止。
注:如果一个PTP_WORK对象多次提交任务,TRUE只会等待当前正在运行的任务,FALSE会等待所有已经任务。

关闭工作项:

VOID CloseThreadpoolWork(PTP_WORK pWork);

eg:用线程池工作项函数实现批处理。
 


# include <iostream>
# include <Windows.h>
# include<threadpoolapiset.h>
using namespace std;
PTP_WORK g_pWorkItem=NULL;
LONG     g_nCurrentWork=0;

VOID WINAPI HandleTask(PTP_CALLBACK_INSTANCE Instance,PVOID Context,PTP_WORK pWork)
{
		LONG CurrentTask = InterlockedIncrement(&g_nCurrentWork);

		cout<<"TASK "<<CurrentTask<<"begin..."<<endl;

		//模拟大量的工作
		Sleep(CurrentTask*1000);

       cout<<"TASK"<<CurrentTask<<"end..."<< endl;

       if(InterlockedDecrement(&g_nCurrentWork)==0){                 //将要递减递加的都代表下一次,当前代表的应该是上一个值
       	cout<<" Work is handled."<< endl;
		}
}

VOID StartBatch()
{
       //提交四个任务用一个相同的工作项
		for(int i=0;i<4;i++){
			SubmitThreadpoolWork(g_pWorkItem);
       }
		cout<<"Four tasks are submitted."<< endl;
}

int main()
{
	g_pWorkItem=CreateThreadpoolWork(HandleTask,NULL,NULL);
  if(g_pWorkItem==NULL){ }

  StartBatch();

  WaitForThreadpoolWorkCallbacks(g_pWorkItem,FALSE);  //等待任务完成才能关闭

  CloseThreadpoolWork(g_pWorkItem);
  return 0;
}

结果如下:

2.每隔一段时间调用一个函数    

PTP_TIMER CreateThreadpoolTimer(PTP_TIMER_CALLBACK pfnTimerCallback,PVOID pvContext,PTP_CALLBACK_ENVIRON pcbe);

和创建工作项的函数类似,传入的函数指针所指的函数原型如下:
VOID CALLBACK TimeoutCallback(PTP_CALLBACK_INSTANCE pINSTANCE,PVOID Context,PTP_TIMER pTimer);

注册计时器,也可以注册后进行修改计时器属性:

VOID SetThreadpoolTimer(PTP_TIMER pTimer,PFILETIME pftDueTime,DWORD msPeriod,DWORD msWindowLength);

 

pTimer:指定的PTP_TIMER对象,创建函数的返回值。
pftDueTime:传入负数,以微秒为单位,为相对时间。-1代表执行函数完立即开始。传入一个正数,绝对时间,从1600.1.1开始计算,单位100ns。
msPeriod:只想让计时器触发一次,传入0。定期调用的时间间隔。非零值,微秒。
msWindowLength:给回调函数的执行时间增添随机性,使得回调函数会在当前设定的触发时间到当前设定触发时间加上这个参数值之间触发。
                当有多个计时器,他们的触发频度几乎相同,为了不希望产生太多的冲突。
                另一个是将多个计时器分成一组,如果有大量的计时器在几乎相同的时间触发,为了避免太多的上下文切换,分成一组更好。可以   设计时器A计时器B的该参数为2,线程池知道计时器A预计它的回调函数在5-7微秒之间调用,计时器B会在6-8微秒内调用,这种情况下线程池知道在同   一时间内6微秒的时候对这两个计时器进行批处理会更加高效。这样线程池只会唤醒一个线程执行计时器A的回调函数然后再执行计时器B的回调函数。

确定计时器是否已经被设置,pftDueTime不为NULL
 

IsThreadpoolTimerSet(PTP_TIMER pti);


这两个函数用法同工作项:

VOID WaitForThreadpoolTimerCallbacks(PTP_TIMER pTimer,BOOL bCancelPendingCallbacks);
VOID CloseThreadpoolTimer(PTP_TIMER pTimer);
eg:
# include <iostream>
# include <Windows.h>
# include <tchar.h>
using namespace std;
TCHAR g_WindowCaption[100]=TEXT("Timer");
LONG g_SelCount=10;
VOID WINAPI MsgBoxCallback(PTP_CALLBACK_INSTANCE pINSTANCE,PVOID Context,PTP_TIMER pTimer) 
{
		HWND hWnd=FindWindow(NULL,g_WindowCaption);
       if(hWnd==NULL){
			return;
       }
		if(g_SelCount==1){return ;}
		TCHAR MsgText[100];

       _stprintf_s(MsgText,_countof(MsgText),TEXT("倒数第%d秒"),--g_SelCount);
       MessageBox(NULL,MsgText,g_WindowCaption,MB_OK);
}
int main()
{
		PTP_TIMER pTimer=CreateThreadpoolTimer(MsgBoxCallback,NULL,NULL);
		if(pTimer==NULL){return 0;};
		
		ULARGE_INTEGER uiRelativeStartTime;
		uiRelativeStartTime.QuadPart = (LONGLONG) -(10000000);   //1s后开始 单位100ns
		FILETIME ftRelativeStartTime;
		ftRelativeStartTime.dwHighDateTime=uiRelativeStartTime.HighPart;
		ftRelativeStartTime.dwLowDateTime=uiRelativeStartTime.LowPart;

		SetThreadpoolTimer(pTimer,&ftRelativeStartTime,1000,0);//间隔1s,单位毫秒

		MessageBox(NULL,TEXT("倒数第10秒."),g_WindowCaption,MB_OK);

       WaitForThreadpoolTimerCallbacks(pTimer,FALSE);

       CloseThreadpoolTimer(pTimer);

		return 0;
}

结果如下:

3.在内核对象触发的时候调用一个函数  

有时候会让多个线程来等待一个对象,这是对系统资源的极端浪费,每个线程都有一个线程栈,并需要大量的CPU指令来创建和销毁线程,所以线程池是不错的。

PTP_WAIT CreateThreadpoolWait(PTP_WAIT_CALLBACK pfnWaitCallack,PVOID pvContext,PTP_CALLBAC_ENVIRON pcbe);

 


pfnWaitCallack:其所指回调函数原型:

VOID WINAPI WaitCallback(PTP_CALLBACK_INSTANCE pInstance,PVOID pvContext,PTP_WAIT Wait,TP_WAIT_RESULT WaitResult);

将一个内核对象绑定到这个线程池:

VOID SetThreadpoolWait(PTP_WAIT pWaitItem,HANDLE hObject,PFILETIME pftTimeOut);


pftTimeOut:传0,根本不用等待;传NULL,无限等待;传负值相对时间;正值绝对时间。

当内核对象被触发或者超时的时候,线程池会调用回调函数。回调函数最后一个参数WaitResult表示回调函数被调用的原因:WAIT_OBJECT_0,内核对象在超时之前被调用,WAIT_TIMEOUT:超时之前未被触发。
如果调用了回调函数则对应的等待项将进入不活跃状态,需要再次调用注册。可以传入一个不同的内核对象,或者传入NULL将该等待从线程池中移除

VOID WaitForThreadpoolWaitCallbacks(PTP_WAIT pWait,BOOL bCancelPendingCallbacks);
VOID CloseThreadpoolWait(PTP_WAIT pWait pTimer);

4.在异步I/O请求完成的时候调用一个函数 

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

hDevice:与线程池内部完成端口相关联的文件/设备句柄。CreateFile的返回值、套接字等。
pfnIoCallback:函数原型如下:

VOID WINAPI OverlappedCompletionRoutine(PTP_CALLBACK_INSTANCE pInstance,PVOID pvContext,PVOID pOverlapped,ULONG IoResult,
                                        ULONG_PTR NumberOfBytesTransferred,PTP_IO pIo);
                                                                                                                                   pOverlapped:得到重叠结构
IoResult:操作结果,I/O成功,返回NO_ERROR
NumberOfBytesTransferred:已经传输的字节数 

线程池I/O对象创建完毕后,通过调用下边函数将嵌入在I/O项中的文件/设备与线程池内部的I/O完成端口关联起来:

VOID StartThreadpoolIo(PTP_IO pIo);   

  如果想在发出I/O请求之后让线程池停止调用我们的回调函数:

VOID CancelThreadpoolIo(PTP_IO pIo);  

 
如果发出请求的时候,ReadFile或者WriteFile调用失败了,仍然必须调用该函数。因为如果这两个函数的返回值FALSE但是GetLastError的返回值为ERROR_IO_PENDING。

当文件设备、套接字使用完成之后,解除与线程池之间的关系:

VOID CloseThreadpoolIo(PTP_IO pIo); 

另一个线程等待带出处理的I/O请求完成:

VOID WaitForThreadpoolIoCallbacks(PTP_IO pIo,BOOL bCancelPendingCallbacks);

bCancelPendingCallbacks:传给TRUE,则当请求完成的时候,回调函数不会被调用(如果它尚未被调用)。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!