您的位置:首页 > 其它

线程池

2016-06-21 17:02 323 查看
线程池的功能
以异步的方式来调用一个函数
每隔一段时间调用一个函数
当内核对象触发的时候调用一个函数
当异步 I/O 请求完成的时候调用一个函数

注意

当一个进程初始化的时候,它并没有任何与线程池的开销.但是,一旦调用了新的线程池函数,系统就会为进程相应的内核资源,其中的一些资源在进程终止之前都将一直存在.正如我们可以看到,使用线程池的开销取决于用法:系统会以进程的名义来分配线程,其他内核以及内部数据结构.因此我们不应该盲目地使用这些线程池函数,而是必须谨慎地考虑,这些函数能做什么,以及它们不能做什么.

在线程池编程中,我们从来不需要自己调用 CreateThread.系统会自动为我们的进程创建线程,并在规定的条件下让线程池中的线程调用我们的回调函数.此外,这个线程在处理完成一个客户请求后,它不会立刻被销毁,而是回到线程池,准备好处理队列中的任何其他工作项,线程池会不断地重复使用其中的线程,而不会频繁地创建销毁线程,对应用程序来说,这样可以显著地提升性能,因为创建和销毁线程会消耗大量的时间.当然,如果线程池检测到创建的另一个线程将能更好地为应用程序服务,那么它会这样做.如果线程池检测到它的线程数量已经供过于求,那么它会销毁其中一些线程.除非我们非常清楚自己在做什么,否则的话最好还是相信线程内部的算法,让它自动地对应用程序的工作量进行微调.

默认线程池,在进程存在期间它不会被销毁.生命周期与进程相同.在进程终止的时候,Windows 会将其销毁并负责所有的清理工作.

对线程池的制定

可以用 CreateThreadpool 来创建一个新的线程池,该函数返回一个
PTP_POOL 值,表示新创建的线程池.接着我们可以调用后面两个函数来设置线程池中线程的最大数量和最小数量 SetThreadpoolThreadMinimum / SetThreadpoolThreadMaximum 线程池始终保持池中的线程数量至少是指定的最小数量,并允许线程数量增长到指定的最大数量,顺便一提,默认线程池的最小数量为1,最大数量为500.然后在引用程序不在需要自定义线程池时,应调用 CloseThreadpool 将其销毁.在调用这个函数后,我们将无法在将任何新的项添加到线程池的队列中.线程池中当前正在处理的队列中的线程会完成它们的处理并终止.此外,线程池的队列中所有尚未开始处理的项将被取消.

一旦我们创建了自己的线程池,并制定了线程池的最小数量和最大数量,我们就可以初始化一个回调环境,它包含了一些课应用于工作项的额外的设置或配置.(线程池回调环境结构 _TP_CALLBACK_ENVIRON ,其定义在 WinNT.h )


View
Code

然后我们可以调用 InitializeThreadpoolEnvironment 初始化这个结构体,接着当然回调环境必须调用 SetThreadpoolCallbackPool 标明给工作项应该由哪个线程池来处理.最后当我们不在需要这个使用线程池回调环境的时候,应该调用 DestroyThreadpoolEnvironment来对它进行清理工作.

然而可以调用 SetThreadpoolCallbackRunsLong 函数来告诉回调环境,工作项通常需要较长的时间来处理.这使得线程池会更快地创建线程,其目的是为了尝试在对工作项进行处理的时候,以一种更为公平的方式来替代最有效的方式.
也可以调用 SetThreadpoolCallbackLibrary 来确保只要线程池中还有待处理的工作项,就将一个特定的
DLL 一直保持在进程空间中.基本上 SetThreadpoolCallbackLibrary 函数的存在目的是为了消除潜在的竞态条件(race
condition),从而避免可能导致死锁.这个相当高级的特性,更详细信息参阅 Platform SDK 文档.

线程池的销毁(清理组)

为了得体地销毁私有线程池,我们首先可以需要通过调用 CreateThreadpoolCleanupGroup 来创建一个清理组,然后再将这个清理组与一个以绑定到线程池的回调函数结构体
TP_CALLBACK_ENVIRON 调用 SetThreadpoolCallbackCleanupGroup 函数把两者关联起来.其中 SetThreadpoolCallbackCleanupGroup 的
pfng 参数标识一个回调函数的地址(函数原型 即CleanupGroupCancelCallback),如果传给
pfng 参数值不为 NULL ,且当清理组被取消时那么这个回调函数会被调用.

当我们调用 CreateThreadpoolWorkCreateThreadpoolTimerCreateThreadpoolWait 或 CreateThreadpoolIo 的时候,如果最后那个参数,即指向
PTP_CALLBACK_ENVIRON 结构体指针,不等于 NULL,那么所创建的项会被添加到对应的回调环境的清理组中,其目的是为了表示有线程池中添加了一项,需要潜在清理.在这些对了项完成后,如果我们调用 CloseThreadpoolWorkCloseThreadpoolTimerCloseThreadpoolWait 和 CloseThreadpoolIo,
那就等于是隐式将对应的项从组中移除.

最后,在程序想要销毁线程池的时候,调用 CloseThreadpoolCleanupGroupMembers.这个函数与下面的
WaitForThreadpool*(例: WaitForThreadpoolWork)函数相似.当线程调用 CloseThreadpoolCleanupGroupMembers 时候,函数会一直等待,知道线程池的工作组中所有剩余的项(即已经创建当尚未关闭的项)都已经处理完毕为止.调用者还可以传
TRUE 给 fCancelPendingCallbacks  参数.这样会将所有已提交但尚未处理的工作项直接取消,函数会在所有当前正在运行的工作项王城之后返回.如果传给 fCancelPendingCallbacks 参数为 TRUE,而且传给 SetThreadpoolCallbackCleanupGroup 的
pfng 参数值是一个 CleanupGroupCancelCallback 函数地址,那么对每一个被取消的工作项,我们的回调函数会被调用,在 CleanupGroupCancelCallback 函数中.参数
ObjectContext 会包含每个被取消的上下文.(该上下文信息是通过 CreateThreadpool* 函数的 pv 参数设置的)在 CleanupGroupCancelCallback 函数中,
参数 CleanupContext 包含的上下文是通过 CloseThreadpoolCleanupGroupMembers 函数的 pvCleanupContext
参数传入的.如果在调用 CloseThreadpoolCleanupGroupMembers 时传入
FALSE 给 fCancelPendingCallbacks 参数.那么在返回之前,线程池会发时间来处理队列中所有剩余的项.注意这种情况下我们的 CleanupGroupCancelCallback 函数绝对不会被调用.因此可以传
NULL 给 pvCleanupContext 参数.

线程池制定与销毁代码样例

编写步骤



#include<Windows.h>
#include<iostream>
#include<cstdlib>

using namespace std;

VOID CALLBACK SimpleCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context);

void main()
{
PTP_POOL tPool;
TP_CALLBACK_ENVIRON pcbe;

//创建线程池
tPool = CreateThreadpool(NULL);

//设置线程池最大最小的线程数量
SetThreadpoolThreadMinimum(tPool,1);
SetThreadpoolThreadMaximum(tPool,2);

//初始化线程池环境变量
InitializeThreadpoolEnvironment(&pcbe);

//为线程池设置线程池环境变量
SetThreadpoolCallbackPool(&pcbe,tPool);

//单次工作提交
TrySubmitThreadpoolCallback(SimpleCallback,NULL,&pcbe);

system("pause");
//清理线程池的环境变量
DestroyThreadpoolEnvironment(&pcbe);
//关闭线程池
CloseThreadpool(tPool);
}

VOID CALLBACK SimpleCallback(PTP_CALLBACK_INSTANCE Instance,PVOID Context)
{
cout<<"this is SimpleCallback function!"<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: