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

 

posted @   TechNomad  阅读(661)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2022-09-18 Qt之QLabel文本内容太长时可以使用省略号
点击右上角即可分享
微信分享提示