Win32编程之线程池(十二)
一、线程池概念介绍
1.线程的执行流程
2.线程池原理
二、线程池异步函数的调用
1.TrySubmitThreadpoolCallback函数
TrySubmitThreadpoolCallback
函数是Windows操作系统提供的一个函数,它用于将回调函数提交到线程池中以异步执行。线程池是一组可用于执行并发任务的线程,它可以帮助提高程序的性能和资源利用率。
函数原型:
1 2 3 4 5 | BOOL TrySubmitThreadpoolCallback( PTP_SIMPLE_CALLBACK pfns, PVOID pv, PTP_CALLBACK_ENVIRON pcbe ); |
参数说明:
pfns
:一个指向回调函数的指针,该函数将在线程池中的工作线程上执行。pv
:传递给回调函数的用户数据指针。pcbe
:一个可选的线程池回调环境,可以用来配置线程池行为。
返回值:
- 如果成功提交回调函数,函数返回非零值。
- 如果线程池队列已满,或者出现其他错误,函数返回零。
使用方法:
(1).首先,需要初始化线程池回调环境(TP_CALLBACK_ENVIRON
)。这可以通过使用 InitializeThreadpoolEnvironment
函数来完成,以确保线程池的配置符合你的需求。
1 2 | TP_CALLBACK_ENVIRON callbackEnviron; InitializeThreadpoolEnvironment(&callbackEnviron); |
(2).创建回调函数。回调函数的原型应与 TP_SIMPLE_CALLBACK
一致,通常如下:
1 2 3 4 | VOID CALLBACK MyCallback(PTP_CALLBACK_INSTANCE instance, PVOID context) { // 在这里执行你的异步任务 } |
(3).使用 TrySubmitThreadpoolCallback
函数提交回调函数到线程池,以异步执行任务:
1 2 3 4 5 6 | BOOL submitted = TrySubmitThreadpoolCallback(MyCallback, userData, &callbackEnviron); if (submitted) { // 成功提交任务到线程池 } else { // 无法提交任务,处理错误 } |
当任务完成后,线程池将调用回调函数,执行你的异步任务。在回调函数中,你可以访问传递的用户数据 (userData
) 并执行所需的操作。
最后,不要忘记清理线程池回调环境(如果有必要):
1 | DestroyThreadpoolEnvironment(&callbackEnviron); |
TrySubmitThreadpoolCallback
函数通常用于异步执行工作,以充分利用系统的多核处理能力。它允许你将工作提交给线程池,而无需自行创建和管理线程。但缺点是每次都需要提交创建新的工作项,每一次创建都会浪费一定的内存而且会损失一定的效率;请确保在使用线程池时小心处理错误情况,并在适当的地方清理资源。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <Windows.h> #include <stdio.h> VOID CALLBACK ThreadpoolCallback(PTP_CALLBACK_INSTANCE instance, PVOID context) { // 在这里执行你的异步任务 printf ( "%s\n" , ( char *)context); } int main() { TP_CALLBACK_ENVIRON callbackEnviron; InitializeThreadpoolEnvironment(&callbackEnviron); char str[] = "Hello world" ; BOOL submitted = TrySubmitThreadpoolCallback(ThreadpoolCallback, str, &callbackEnviron); if (submitted) { printf ( "执行成功\n" ); } DestroyThreadpoolEnvironment(&callbackEnviron); return 1; } |
2.CreateThreadpoolWork函数
CreateThreadpoolWork
函数是 Windows 操作系统提供的函数,用于创建线程池工作对象,这些对象可用于将工作项提交到线程池中执行。线程池工作对象代表一个可执行的工作项,该工作项通常由线程池中的工作线程执行。
函数原型:
1 2 3 4 5 | PTP_WORK CreateThreadpoolWork( PTP_WORK_CALLBACK pfnwk, PVOID pv, PTP_CALLBACK_ENVIRON pcbe ); |
参数说明:
pfnwk
:一个指向工作项回调函数的指针,该函数将在线程池中的工作线程上执行。pv
:传递给工作项回调函数的用户数据指针。pcbe
:一个可选的线程池回调环境,用于配置工作项的行为。
返回值:
- 如果成功创建工作项对象,函数返回一个指向
TP_WORK
结构的指针(PTP_WORK
类型)。 - 如果出现错误,函数返回 NULL。
使用方法:
首先,需要初始化线程池回调环境(TP_CALLBACK_ENVIRON
)。这可以通过使用 InitializeThreadpoolEnvironment
函数来完成,以确保线程池的配置符合你的需求。
1 2 | TP_CALLBACK_ENVIRON callbackEnviron; InitializeThreadpoolEnvironment(&callbackEnviron); |
创建工作项回调函数。回调函数的原型应与 PTP_WORK_CALLBACK
一致,通常如下:
1 2 3 4 | VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work) { // 在这里执行你的工作项任务 } |
使用 CreateThreadpoolWork
函数创建工作项对象,并将其绑定到回调函数:
1 2 3 4 | PTP_WORK workItem = CreateThreadpoolWork(MyWorkCallback, userData, &callbackEnviron); if (workItem == NULL) { // 处理工作项创建失败的错误 } |
使用 SubmitThreadpoolWork
函数将工作项提交到线程池,以异步执行任务:
1 | SubmitThreadpoolWork(workItem); |
当线程池中的工作线程可用时,它们将调用回调函数 MyWorkCallback
来执行工作项任务。在回调函数中,你可以访问传递的用户数据 (userData
) 并执行所需的操作。在工作项完成后,你可以通过调用 CloseThreadpoolWork
函数来关闭工作项对象,并在不再需要时释放资源:
1 | CloseThreadpoolWork(workItem); |
CreateThreadpoolWork
和相关的线程池函数通常用于异步执行工作,以提高程序性能并充分利用系统的多核处理能力。线程池可以自动管理线程的创建和销毁,使得线程管理更加高效。确保适时关闭和释放线程池工作项以防止资源泄漏。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <Windows.h> #include <stdio.h> VOID CALLBACK ThreadpoolWork(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work) { printf ( "%s\n" , ( char *)Context); } int main() { char str[] = "hello world" ; PTP_WORK workitem = CreateThreadpoolWork(ThreadpoolWork, str, NULL); if (workitem == NULL) { printf ( "CreateThreadpoolWork Failed!\n" ); return 0; } SubmitThreadpoolWork(workitem); SubmitThreadpoolWork(workitem); SubmitThreadpoolWork(workitem); SubmitThreadpoolWork(workitem); CloseThreadpoolWork(workitem); return 1; } |
三、线程池的周期性调用
1.CreateThreadpoolTimer()函数
CreateThreadpoolTimer
函数是 Windows API 中的一个函数,用于创建一个线程池定时器对象。线程池定时器可用于在指定时间间隔后执行回调函数。这有助于避免创建单独的线程来执行定时任务,提高了性能和资源利用率。
函数原型:
1 2 3 4 5 | PTP_TIMER CreateThreadpoolTimer( PTP_TIMER_CALLBACK pfn, PVOID pv, PTP_CALLBACK_ENVIRON pcbe ); |
参数解释:
pfn
:指向定时器回调函数的指针。该回调函数将在定时器到期时被调用。pv
:可选参数,指向传递给回调函数的上下文数据的指针。pcbe
:可选参数,指向线程池回调环境的指针,可用于自定义线程池行为。可以传递NULL
。
返回值:
如果函数调用成功,将返回指向新创建的线程池定时器对象的指针(PTP_TIMER
)。如果函数调用失败,将返回 NULL
,您可以通过调用 GetLastError
获取错误信息。
2.SetThreadpoolTimer函数
SetThreadpoolTimer
函数是 Windows API 中用于更改线程池定时器的触发时间和间隔的函数。线程池定时器是一种高效的机制,可用于在指定时间间隔后执行回调函数。
函数原型:
1 2 3 4 5 6 | VOID SetThreadpoolTimer( PTP_TIMER pti, PFILETIME pftDueTime, DWORD msPeriod, DWORD msWindowLength ); |
参数解释:
pti
:指向线程池定时器的指针,通过CreateThreadpoolTimer
函数创建。pftDueTime
:指向FILETIME
结构的指针,用于指定定时器的下一次触发时间。可以为NULL
,表示立即触发。msPeriod
:定时器的触发周期,以毫秒为单位。如果设置为0,则定时器将变为单次触发,不会重复。msWindowLength
:可选参数,指定触发时间的窗口长度(在msPeriod
基础上),用于控制定时器的灵活性。可以设置为0。该参数为参数三的周期性提供一个随机延迟时间值,能够避免多计时器同频回调冲突,也能避免多定时器相近时段上下文切换。
3.CloseThreadpoolTimer函数
CloseThreadpoolTimer
函数是 Windows API 中用于关闭线程池定时器的函数。线程池定时器是一种高效的机制,可用于在指定时间间隔后执行回调函数。在不再需要定时器时,应使用 CloseThreadpoolTimer
函数来释放相关资源。
函数原型:
1 2 3 | VOID CloseThreadpoolTimer( PTP_TIMER ptimer ); |
参数解释:
ptimer
:指向要关闭的线程池定时器的指针,通过CreateThreadpoolTimer
函数创建。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <Windows.h> #include <stdio.h> VOID NTAPI PoolTimerProc(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer) { printf ( "Hello World\n" ); } int main() { PTP_TIMER lpTimer = CreateThreadpoolTimer(PoolTimerProc, NULL, NULL); FILETIME dueTime; GetSystemTimeAsFileTime(&dueTime); SetThreadpoolTimer(lpTimer, &dueTime, 1000, 0); system ( "pause" ); CloseThreadpoolTimer(lpTimer); return 1; } |
四、线程池内核对象触发调用
1.CreateThreadpoolWait函数
CreateThreadpoolWait
函数是 Windows API 中用于创建线程池等待对象的函数。线程池等待对象用于等待一个或多个事件或状态的发生,并在事件发生时执行指定的回调函数。这是一种有效的机制,用于异步事件处理。
函数原型:
1 2 3 4 5 | PTP_WAIT CreateThreadpoolWait( PTP_WAIT_CALLBACK pfnwaithint, PVOID pv, PTP_CALLBACK_ENVIRON pcbe ); |
参数解释:
pfnwaithint
:指向等待回调函数的指针,当等待对象的状态发生变化时,将调用此回调函数。pv
:可选参数,指向传递给回调函数的上下文数据的指针。pcbe
:可选参数,指向线程池回调环境的指针,可用于自定义线程池行为。可以传递NULL
。
返回值:
如果函数调用成功,将返回指向新创建的线程池等待对象的指针(PTP_WAIT
)。如果函数调用失败,将返回 NULL
,您可以通过调用 GetLastError
获取错误信息。
2.SetThreadpoolWait函数
SetThreadpoolWait
函数是 Windows API 中用于配置线程池等待对象的函数。线程池等待对象用于等待一个或多个事件或状态的发生,并在事件发生时执行指定的回调函数。使用 SetThreadpoolWait
函数,您可以将等待对象与事件或状态关联,并设置回调函数。
函数原型:
1 2 3 4 5 | VOID SetThreadpoolWait( PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout, ); |
参数解释:
pwa
:指向线程池等待对象的指针,通过CreateThreadpoolWait
函数创建。h
:要等待的事件句柄。可以是事件、互斥体等内核对象的句柄。pftTimeout
:可选参数,指向FILETIME
结构的指针,用于设置等待的超时时间。如果不希望设置超时时间,可以传递NULL
。
3. CloseThreadpoolWait函数
CloseThreadpoolWait
函数是 Windows API 中用于关闭线程池等待对象的函数。线程池等待对象用于等待一个或多个事件或状态的发生,并在事件发生时执行指定的回调函数。当不再需要等待对象时,应使用 CloseThreadpoolWait
函数来释放相关资源。
函数原型:
1 2 3 | VOID CloseThreadpoolWait( PTP_WAIT pwa ); |
参数解释:
pwa
:指向要关闭的线程池等待对象的指针,通过CreateThreadpoolWait
函数创建。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <Windows.h> #include <iostream> // 等待回调函数 VOID CALLBACK WaitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WAIT wait, TP_WAIT_RESULT waitResult) { printf ( "Hello World\n" ); } int main() { HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //超时时间设置为NULL,表示一直等待下去 PTP_WAIT pWait = CreateThreadpoolWait(WaitCallback, NULL, NULL); //立即执行 ULARGE_INTEGER ulRelativeStartTime; ulRelativeStartTime.QuadPart = 0; FILETIME ftRelativeStartTime; ftRelativeStartTime.dwHighDateTime = ulRelativeStartTime.HighPart; ftRelativeStartTime.dwLowDateTime = ulRelativeStartTime.LowPart; SetThreadpoolWait(pWait, hEvent, &ftRelativeStartTime); //等候五秒再触发 /*ULARGE_INTEGER ulRelativeStartTime; ulRelativeStartTime.QuadPart = (LONGLONG)-(50000000); FILETIME ftRelativeStartTime; ftRelativeStartTime.dwHighDateTime = ulRelativeStartTime.HighPart; ftRelativeStartTime.dwLowDateTime = ulRelativeStartTime.LowPart; SetThreadpoolWait(pWait, hEvent, &ftRelativeStartTime);*/ //超时时间为NULL,表示一直等待下去,直到事件触发 //SetThreadpoolWait(pWait, hEvent, NULL); getchar (); SetEvent(hEvent); getchar (); CloseThreadpoolWait(pWait); return 0; } |
五、线程池IO完成调用
1.CreateThreadpoolIo函数
CreateThreadpoolIo
函数是 Windows API 中用于创建线程池 I/O 对象的函数。线程池 I/O 对象可用于异步 I/O 操作的管理,以提高性能和资源利用率。
函数原型:
1 2 3 4 5 6 | PTP_IO CreateThreadpoolIo( HANDLE fl, PTP_WIN32_IO_CALLBACK pfnio, PVOID pv, PTP_CALLBACK_ENVIRON pcbe ); |
参数解释:
fl
:要与线程池 I/O 对象关联的文件句柄。pfnio
:指向 I/O 完成回调函数的指针。该回调函数在 I/O 操作完成时被调用。pv
:可选参数,指向传递给回调函数的上下文数据的指针。pcbe
:可选参数,指向线程池回调环境的指针,可用于自定义线程池行为。可以传递NULL
。
返回值:
如果函数调用成功,将返回指向新创建的线程池 I/O 对象的指针(PTP_IO
)。如果函数调用失败,将返回 NULL
,您可以通过调用 GetLastError
获取错误信息。
2.StartThreadpoolIo函数
StartThreadpoolIo
函数是 Windows API 中用于启动异步 I/O 操作的函数。它告诉线程池监视指定的 I/O 对象,并在 I/O 操作完成时调用回调函数。这个函数通常与 WaitForSingleObject
或其他等待函数一起使用,以等待 I/O 操作的完成。
函数原型:
1 2 3 | VOID StartThreadpoolIo( PTP_IO pio ); |
参数解释:
pio
:指向线程池 I/O 对象的指针,通过CreateThreadpoolIo
函数创建。
3.CloseThreadpoolIo函数
CloseThreadpoolIo
函数是 Windows API 中用于关闭线程池 I/O 对象的函数。线程池 I/O 对象用于异步 I/O 操作的管理。当不再需要线程池 I/O 对象时,应使用 CloseThreadpoolIo
函数来释放相关资源。
函数原型:
1 2 3 | VOID CloseThreadpoolIo( PTP_IO pio ); |
参数解释:
pio
:指向要关闭的线程池 I/O 对象的指针,通过CreateThreadpoolIo
函数创建。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <Windows.h> #include <stdio.h> VOID CALLBACK IoCallback( PTP_CALLBACK_INSTANCE instance, PVOID context, PVOID overlapped, ULONG ioResult, ULONG_PTR numberOfBytesTransferred, PTP_IO io) { printf ( "文件写入完成\n" ); } int main() { HANDLE hFile = CreateFile(TEXT( "test.txt" ), GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); if (hFile == INVALID_HANDLE_VALUE) { printf ( "create file failed, errcode:%d\n" , GetLastError()); } PTP_IO ptpIo = CreateThreadpoolIo(hFile, IoCallback, NULL, NULL); StartThreadpoolIo(ptpIo); char buffer[] = "hello world" ; DWORD writeCount = 0; OVERLAPPED ol = { 0 }; WriteFile(hFile, ( LPCVOID )buffer, strlen (buffer), &writeCount, &ol); CloseHandle(hFile); getchar (); CloseThreadpoolIo(ptpIo); return 1; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2022-09-18 Qt之QLabel文本内容太长时可以使用省略号