线程池
(本章节中例子都是用 VS2010 编译调试的)
线程池编写必须在 Windows Vista 操作系统(以及以上版本的操作系统)下,且 C++ 编译器版本至少是 VS2008
线程池的功能
- 以异步的方式来调用一个函数
- 每隔一段时间调用一个函数
- 当内核对象触发的时候调用一个函数
- 当异步 I/O 请求完成的时候调用一个函数
注意
当一个进程初始化的时候,它并没有任何与线程池的开销.但是,一旦调用了新的线程池函数,系统就会为进程相应的内核资源,其中的一些资源在进程终止之前都将一直存在.正如我们可以看到,使用线程池的开销取决于用法:系统会以进程的名义来分配线程,其他内核以及内部数据结构.因此我们不应该盲目地使用这些线程池函数,而是必须谨慎地考虑,这些函数能做什么,以及它们不能做什么.
在线程池编程中,我们从来不需要自己调用 CreateThread.系统会自动为我们的进程创建线程,并在规定的条件下让线程池中的线程调用我们的回调函数.此外,这个线程在处理完成一个客户请求后,它不会立刻被销毁,而是回到线程池,准备好处理队列中的任何其他工作项,线程池会不断地重复使用其中的线程,而不会频繁地创建销毁线程,对应用程序来说,这样可以显著地提升性能,因为创建和销毁线程会消耗大量的时间.当然,如果线程池检测到创建的另一个线程将能更好地为应用程序服务,那么它会这样做.如果线程池检测到它的线程数量已经供过于求,那么它会销毁其中一些线程.除非我们非常清楚自己在做什么,否则的话最好还是相信线程内部的算法,让它自动地对应用程序的工作量进行微调.
默认线程池,在进程存在期间它不会被销毁.生命周期与进程相同.在进程终止的时候,Windows 会将其销毁并负责所有的清理工作.
对线程池的制定
可以用 CreateThreadpool 来创建一个新的线程池,该函数返回一个 PTP_POOL 值,表示新创建的线程池.接着我们可以调用后面两个函数来设置线程池中线程的最大数量和最小数量 SetThreadpoolThreadMinimum / SetThreadpoolThreadMaximum 线程池始终保持池中的线程数量至少是指定的最小数量,并允许线程数量增长到指定的最大数量,顺便一提,默认线程池的最小数量为1,最大数量为500.然后在引用程序不在需要自定义线程池时,应调用 CloseThreadpool 将其销毁.在调用这个函数后,我们将无法在将任何新的项添加到线程池的队列中.线程池中当前正在处理的队列中的线程会完成它们的处理并终止.此外,线程池的队列中所有尚未开始处理的项将被取消.
一旦我们创建了自己的线程池,并制定了线程池的最小数量和最大数量,我们就可以初始化一个回调环境,它包含了一些课应用于工作项的额外的设置或配置.(线程池回调环境结构 _TP_CALLBACK_ENVIRON ,其定义在 WinNT.h )
/*************************************** 显示此结果的编译环境为 VS2010 ***************************************/ #if (_WIN32_WINNT >= _WIN32_WINNT_WIN7) typedef struct _TP_CALLBACK_ENVIRON_V3 { TP_VERSION Version; PTP_POOL Pool; PTP_CLEANUP_GROUP CleanupGroup; PTP_CLEANUP_GROUP_CANCEL_CALLBACK CleanupGroupCancelCallback; PVOID RaceDll; struct _ACTIVATION_CONTEXT *ActivationContext; PTP_SIMPLE_CALLBACK FinalizationCallback; union { DWORD Flags; struct { DWORD LongFunction : 1; DWORD Persistent : 1; DWORD Private : 30; } s; } u; TP_CALLBACK_PRIORITY CallbackPriority; DWORD Size; } TP_CALLBACK_ENVIRON_V3; typedef TP_CALLBACK_ENVIRON_V3 TP_CALLBACK_ENVIRON, *PTP_CALLBACK_ENVIRON; #else typedef struct _TP_CALLBACK_ENVIRON_V1 { TP_VERSION Version; PTP_POOL Pool; PTP_CLEANUP_GROUP CleanupGroup; PTP_CLEANUP_GROUP_CANCEL_CALLBACK CleanupGroupCancelCallback; PVOID RaceDll; struct _ACTIVATION_CONTEXT *ActivationContext; PTP_SIMPLE_CALLBACK FinalizationCallback; union { DWORD Flags; struct { DWORD LongFunction : 1; DWORD Persistent : 1; DWORD Private : 30; } s; } u; } TP_CALLBACK_ENVIRON_V1; typedef TP_CALLBACK_ENVIRON_V1 TP_CALLBACK_ENVIRON, *PTP_CALLBACK_ENVIRON; #endif
然后我们可以调用 InitializeThreadpoolEnvironment 初始化这个结构体,接着当然回调环境必须调用 SetThreadpoolCallbackPool 标明给工作项应该由哪个线程池来处理.最后当我们不在需要这个使用线程池回调环境的时候,应该调用