线程池(代码简单分析)
http://blog.csdn.net/loveghb/archive/2006/01/04/569850.aspx
线程池(代码简单分析)
// CThreadPool
// 这个类是一个简单的基于IO完成端口的线程池
// Worker:
// 这个类负责处理线程池里的请求
// 它必须有一个RequestType的类型定义,这个数据类型用来在线程池内做为请求来排队
// RequestType必需强制转换成DWORD
// -1是个保留值,表示关闭线程池
// Worker must also have a void Execute(RequestType request, void *pvParam, OVERLAPPED *pOverlapped) function
// ThreadTraits:
// 这个类实现了一个静态的CreateThread函数,你可以重写一个来决定你的线程如何被创建
我们先来看ThreadTraits,它决定了如何创建线程,CThreadPool内部调用了ThreadTraits::CreateThread,ThreadTraits有两个版本,通过宏定义来决定到底取用哪个。我们来看一下这2个不同的ThreadTraits
class Win32ThreadTraits
{
public:
static HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpsa, DWORD dwStackSize, LPTHREAD_START_ROUTINE pfnThreadProc, void *pvParam, DWORD dwCreationFlags, DWORD *pdwThreadId) throw()
{
return ::CreateThread(lpsa, dwStackSize, pfnThreadProc, pvParam, dwCreationFlags, pdwThreadId);
}
};
class CRTThreadTraits
{
public:
static HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpsa, DWORD dwStackSize, LPTHREAD_START_ROUTINE pfnThreadProc, void *pvParam, DWORD dwCreationFlags, DWORD *pdwThreadId) throw()
{
ATLASSERT(sizeof(DWORD) == sizeof(unsigned int)); // sanity check for pdwThreadId
// _beginthreadex calls CreateThread which will set the last error value before it returns.
return (HANDLE) _beginthreadex(lpsa, dwStackSize, (unsigned int (__stdcall *)(void *)) pfnThreadProc, pvParam, dwCreationFlags, (unsigned int *) pdwThreadId);
}
};
不同之处在于一个调用了_ beginthreadex,另一个调用了CreateThread。
第一个参数Worker可以参考我前面一篇写的demo
CthreadPool实现了IThreadPoolConfig接口,主要用来做一些线程池的配置,只有4个方法,如下:
SetSize(int nNumThreads)
GetSize(int *pnNumThreads)
SetTimeout(DWORD dwMaxWait)
GetTimeout(DWORD *pdwMaxWait)
下面介绍一些CthreadPool类的一些成员变量:
CSimpleMap<DWORD, HANDLE> m_threadMap; //把线程的id和handle关联起来
DWORD m_dwThreadEventId; //前一个被处理的线程id
CComCriticalSection m_critSec; //临界区
DWORD m_dwStackSize; //每个线程的栈的大小
DWORD m_dwMaxWait; //线程池最大的等待时间
void *m_pvWorkerParam; //每个线程的额外参数
LONG m_bShutdown; //线程池是否被关闭的标志
HANDLE m_hThreadEvent; //event对象
HANDLE m_hRequestQueue; //完成端口handle
我们先从线程池的构造、线程等待、执行,线程池关闭一系列动作来分析代码吧。
构造函数只是简单的初始化线程池内的一些成员变量,此时内部是没有工作线程存在的。
接着我们调用线程池的Initialize函数,原形如下:
HRESULT Initialize(
void * pvWorkerParam = NULL,
int nNumThreads = 0,
DWORD dwStackSize = 0,
HANDLE hCompletion = INVALID_HANDLE_VALUE
) throw( );
第1个参数用来传给工作线程的Initialize、Execute、Terminate三个函数,第2个参数表示线程池内默认的工作线程数,第3个参数是每个工作线程的栈大小。第4个参数是跟完成端口关联的一个handle。
Initialize函数内部创建了一个完成端口,并且创建了nNumThreads个工作线程。
内部的工作线程调度是通过在线程创建的时候把一个static的成员函数通过传递一个this指针来实现的J 很常见的做法。
通过
static DWORD WINAPI WorkerThreadProc(LPVOID pv)
来调用
DWORD ThreadProc()
在ThreadProc函数内部通过循环等待完成端口的请求,并处理。
工作线程等待的请求是通过CThreadPool的BOOL QueueRequest(typename Worker::RequestType request)函数来完成的,内部是通过PostQueuedCompletionStatus向完成端口发送一个包。
接着我们来看看线程池的关闭:
void Shutdown(DWORD dwMaxWait=0);
此函数内部调用InternalResizePool函数来等待并结束所有的工作线程,来关闭线程池。
InternalResizePool函数内部通过完成端口向线程发送结束的命令来通知所有线程来关闭,如果关闭失败,最后会调用TerminateThread来强制关闭线程。
Ps:写的太简单了,有兴趣的自己看源代码吧