自ATL7开始,它就提供了一个叫做CThreadPool 的类,这是一个模板类,基于完成端口开发的一个线程池。它的使用并不麻烦,现在已经是ATL8了,它里面的BUG也已经得到了修正。这个模板类有一个不好的地方就是它要求调用的请求函数参数是相同的,也就是说在它的生存期它只能安排一模一样的请求任务。在这里为了方便起见,统一称安排到线程池里执行的线程为请求函数。CThreadPool在安排多样任务这一点比不上QueueUserWorkItem,QueueUserWorkItem的原型是这样的:
BOOL QueueUserWorkItem(
LPTHREAD_START_ROUTINE Function,
PVOID Context,
ULONG Flags );
Function指明请求函数,Context指明请求函数的参数,我很少用QueueUserWorkItem,毕竟它太不透明了。 在安排大量而又长时间的任务时,我一直使用自己写的线程池,虽然它的速度没有WINDOWS提供的线程池那么快,但是稳定可靠,我可以保证它安稳地工作,不出任何问题。
而现在,又多了一种新的选择,ATL提供的CThreadPool还提供了源代码,有什么问题你可以自己通过修改源代码来解决这个BUG。它只是基于完成端口并使用了一些EVENT还有锁。这都是很成熟的东西了,完全可以放心地使用它。
CThreadPool不好的地方就是它基于模板,并且基于完成端口,完全不适合新手使用。有一些工作多年但是写C比较多的,对它的使用也是觉得为难。另外,它还只能接受一种被调用的请求函数,如果你要调用其它的请求函数,就必须重新Initialize。那么该如何使用它呢?我以MSDN的一个例子来说明。
CThreadPool Sample: Demonstrates How to Use a Thread Pool
msdn.microsoft.com/en-us/library/39f8fee2(VS.80).aspx
这个例子说明如何使用CThreadPool,它很简单,如果你要在项目中使用的话,还要做许多工作。上面有源代码可以下载,下载后解压可以看到。里面的文件很少,就只有
tasks.h work.h ThreadPool.cpp stdafx.h stdafx.cpp
先从stdafx.h说起,
#include <atlutil.h>, CThreadPool类的定义位于这个文件中。
#define WIN32_LEAN_AND_MEAN 这个是为了加速头文件处理所用到的宏。
ThreadPool.cpp,简化该文件中的代码,它所做的工作无非如下:
CThreadPool<CMyWorker> pool;
HRESULT hr = pool.Initialize((void*)321, THREADPOOL_SIZE);
if( SUCCEEDED( hr ) ) {
pool.QueueRequest( (CMyWorker::RequestType) pTask );
}
// Allow a little time for all the tasks to complete
Sleep(1000);
// Clean up the tasks and shutdown the thread pool
for ( i = 0; i < tasks.GetSize(); i++ ) {
pTask = tasks[ i ];
delete pTask;
}
// Shutdown the thread pool
pool.Shutdown();
下面来解释一下 pool.Initialize((void*)321, THREADPOOL_SIZE); 这里的321是请求函数的参数,THREADPOOL_SIZE指明线程池有几个线程,Initialize内部建立一个完成端口。QueueRequest 投递执行行请求,在QueueRequest的内部使用PostQueuedCompletionStatus向完成端口投递请求,当线池里的线程GetQueuedCompletionStatus时,就会收到这个请求,然后调用外部传进来的请求函数,所以说CThreadPool用完成端口完成了线程池内部的同步问题。因此QueueRequest 调用并不代表它所安排的请求函数已经被调用了。使用完以后有一些清理工作,pool.Shutdown();
必须要注意的是CThreadPool<CMyWorker> pool;中的CMyWorker,MSDN中定义了它的原型,它必须包含下面所列的几个函数Initialize,Execute,Terminate。
class CMyWorker
{
public:
typedef MyRequestType RequestType;
BOOL Initialize(void* pvWorkerParam);
void Execute(
MyRequestType request,
void* pvWorkerParam,
OVERLAPPED* pOverlapped
);
void Terminate(void* pvWorkerParam);
};
在worker.h中你可以看到CMyWorker的定义包含了这几个函数。其中Execute做的工作就是将请求函数执行。这里才是真正执行函数的地方。
对于tasks.h我觉得没有什么可以解释的,从源代码上你可以看到它的一切。