Windows thread pool functions

Posted by pikemsu28 on Tue, 04 Feb 2020 12:36:38 +0100

Windows provides a thread pool mechanism matching the completion port.

1. Call function / work item asynchronously
2. Call a function every other time / / timing item
3. Call a function when the kernel object is triggered / / wait item
4. Call a function / / I/O item when the asynchronous I/O request is completed

1. Call functions asynchronously

Create a work item and submit tasks multiple times.

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

pvContext: value passed to callback function
pcbe: related to the customization of thread pool
pfnWorkHandler: function pointer, the function prototype of the function looks like this
Viod callback workcallback (PTP > callback > instance, pvoid context, PTP > work); / / work: return value of function creation

Submit a task request to the thread pool:
 

VOID SubmitThreadpoolWork(PTP_WORK Work);

If you need to submit a work item multiple times, the value of the incoming Context must be the same each time the callback function executes.

In another thread, you want to cancel or wait for the work item to finish processing before suspending the thread:
 

VOID WaitForThreadpoolWorkCallbacks(PTP_WORK pWork,BOOL bCancelPendingCallbacks);

pWork: the specified waiting work item
bCancelPendingCallbacks: TRUE: attempts to cancel the previously submitted work item. If the thread is processing the work item, it will not be interrupted until the work item is completed. If the submitted work item has not been processed by any threads, the function marks it as cancelled and returns it immediately. When the completion port fetches the work item, the thread pool does not call the callback function. FALSE: the thread is suspended until the work item is completed and the thread processing the work item is ready to process the next work item bit.
Note: if a PTP work object submits tasks multiple times, TRUE will only wait for the currently running tasks, and FALSE will wait for all the existing tasks.

Close work item:

VOID CloseThreadpoolWork(PTP_WORK pWork);

eg: implement batch processing with thread pool work item function.
 

# 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;

		//Simulate a lot of work
		Sleep(CurrentTask*1000);

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

       if(InterlockedDecrement(&g_nCurrentWork)==0){                 //What will be decremented and incremented represents the next time, and the current one should represent the previous value
       	cout<<" Work is handled."<< endl;
		}
}

VOID StartBatch()
{
       //Submit four tasks with the same work item
		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);  //Wait for the task to complete before closing

  CloseThreadpoolWork(g_pWorkItem);
  return 0;
}

The results are as follows:

2. Call a function every other time

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

Similar to the function for creating work items, the function prototype referred to by the function pointer passed in is as follows:
VOID CALLBACK TimeoutCallback(PTP_CALLBACK_INSTANCE pINSTANCE,PVOID Context,PTP_TIMER pTimer);

Register the timer, or modify the timer properties after registration:

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

 

pTimer: the specified PTP ﹣ timer object, which creates the return value of the function.
pftDueTime: negative number in microseconds, relative time. -1 means to start immediately after executing the function. Enter a positive number, absolute time, starting from 1600.1.1, unit: 100ns.
msPeriod: you only want the timer to trigger once, passing in 0. The interval between periodic calls. Nonzero, microseconds.
msWindowLength: add randomness to the execution time of the callback function, so that the callback function will trigger between the currently set trigger time and the currently set trigger time plus this parameter value.
When there are multiple timers, their trigger frequency is almost the same, in order to avoid too many conflicts.
The other is to divide multiple timers into a group. If a large number of timers are triggered at almost the same time, it is better to divide them into a group to avoid too many context switches. It can be designed that the parameter of timer B of timer A is 2. The thread pool knows that timer a expects its callback function to be called between 5-7 microseconds, and timer B will be called within 6-8 microseconds. In this case, the thread pool knows that it will be more efficient to batch these two timers at 6 microseconds in the same time. In this way, the thread pool will only wake up one thread to execute the callback function of timer A and then execute the callback function of timer B.

Determine whether the timer has been set, pftDueTime is not NULL
 

IsThreadpoolTimerSet(PTP_TIMER pti);


The usage of these two functions is the same as that of work item:

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("Countdown%d second"),--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);   //100ns after 1s
		FILETIME ftRelativeStartTime;
		ftRelativeStartTime.dwHighDateTime=uiRelativeStartTime.HighPart;
		ftRelativeStartTime.dwLowDateTime=uiRelativeStartTime.LowPart;

		SetThreadpoolTimer(pTimer,&ftRelativeStartTime,1000,0);//Interval 1s, in milliseconds

		MessageBox(NULL,TEXT("Last 10 seconds."),g_WindowCaption,MB_OK);

       WaitForThreadpoolTimerCallbacks(pTimer,FALSE);

       CloseThreadpoolTimer(pTimer);

		return 0;
}

The results are as follows:

3. Call a function when the kernel object is triggered

Sometimes multiple threads will wait for an object, which is an extreme waste of system resources. Each thread has a thread stack and requires a large number of CPU instructions to create and destroy threads, so the thread pool is good.

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

 


Pfnwaitcallback: the prototype of the callback function it refers to:

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

Bind a kernel object to this thread pool:

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


pftTimeOut: pass 0, no waiting at all; pass NULL, infinite waiting; pass negative relative time; positive absolute time.

When the kernel object is triggered or timed out, the thread pool calls the callback function. WaitResult, the last parameter of the callback function, indicates the reason why the callback function was called: wait? Object? 0, the kernel object was called before the timeout, wait? Timeout: it was not triggered before the timeout.
If the callback function is called, the corresponding wait item will enter the inactive state, and the registration needs to be called again. You can either pass in a different kernel object or NULL to remove the wait from the thread pool

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

4. Call a function when the asynchronous I/O request is completed

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

hDevice: the file / device handle associated with the internal completion port of the thread pool. Return value of CreateFile, socket, etc.
pfnIoCallback: the function prototype is as follows:

VOID WINAPI OverlappedCompletionRoutine(PTP_CALLBACK_INSTANCE pInstance,PVOID pvContext,PVOID pOverlapped,ULONG IoResult,
                                        ULONG_PTR NumberOfBytesTransferred,PTP_IO pIo);
Ploverlapped: get overlapped structure
IoResult: operation result, I/O success, return no error
Numberofbytesttransferred: number of bytes transferred

After the thread pool I/O object is created, the file / device embedded in the I/O item is associated with the I/O completion port inside the thread pool by calling the following function:

VOID StartThreadpoolIo(PTP_IO pIo);   

If you want the thread pool to stop calling our callback function after I/O request:

VOID CancelThreadpoolIo(PTP_IO pIo);  

 
If the ReadFile or WriteFile call fails when the request is made, the function must still be called. Because if the return value of these two functions is FALSE, but the return value of GetLastError is error? IO? Pending.

When the file device and socket are used, the relationship with the thread pool is released:

VOID CloseThreadpoolIo(PTP_IO pIo); 

Another thread waits for the I/O request to be processed out of the belt to complete:

VOID WaitForThreadpoolIoCallbacks(PTP_IO pIo,BOOL bCancelPendingCallbacks);

bCancelPendingCallbacks: passed to TRUE, the callback function will not be called when the request completes (if it has not already been called).

Published 39 original articles, won praise 12, visited 9595
Private letter follow

Topics: Windows socket