contributed by DavidRipple 2005/04/11
1. 什么是线程池?
线程池就是一堆的线程的管理器。为什么需要它呢?通常为了提供应用的响应速度,我们都喜欢为每个业务处理开一个线程。在正常情况下,这些线程是可管理的。但是,但系统有突发的异常事务流时,如果还狂开线程就可能会导致系统资源耗尽或系统崩溃,起码也会造成系统不稳定。线程池就是对线程的数量和状态进行实时监控,通过设置最大线程数量和平衡线程数量来使线程的使用平缓化,努力使整个系统中的线程数量在我们预先设定的平衡线程数量附近摆动。
2. 基于完成端口的线程池的设计思想
以前看过<>,里面有一个线程池的实现。但那个实现可能是采取EVENT来实现的,我不大记得了但最近看了基于完成端口的实现,感觉思路非常清晰。其基本思想如下:
系统创建了两个完成端口,一个用于管理线程,一个为众多的工作者线程所共享。我们为每一个工组者线程关联一个线程的状态结构,利用std::map建立一个线程ID到这个状态的映射。管理线程每隔几秒钟便检查系统中的线程状态,进行线程数量的动态调整。工作者线程就不断的在绑定的完成端口上等待任务,或管理线程发过来的管理命令,并贯彻执行。
3. 用完成端口来设计线程池有哪些优点?
优点真是太多了,呵呵^_^,首先完成线程支持等待超时,可被管理线程用来做周期处理;其次,我们可以自己订制完成消息通过PostQueuedCompletionStatus来发送,任何完成线程都可能接受到这个消息;PostQueuedCompletionStatus发过来的消息只能被一个完成线程消费,并且消费此消息的完成线程当时肯定是空闲的,当时正埋头工作的完成线程是接收不到这个完成消息。这个特性正好可以被用于系统的线程数量的动态调整。Fantastic!!!!
4. 代码解释
管理线程实现:
unsigned __stdcall TThreadPool::ManagerProc(void* p)
{
TThreadPool* pServer = (TThreadPool*) p;
HANDLE IoPort = pServer->GetMgrIoPort();
unsigned long pN1, pN2;
OVERLAPPED* pOverLapped;
BOOL bExit = FALSE;
LABEL_MANAGER_PROCESSING:
while (::GetQueuedCompletionStatus(IoPort, &pN1, &pN2, &pOverLapped, pServer->GetMgrWaitTime()))
{
if (pOverLapped == (OVERLAPPED*) 0xFFFFFFFF)
{
bExit = TRUE;
break;
}
}
if (::GetLastError() == WAIT_TIMEOUT && !bExit)
{
if (pServer->GetThreadPoolStatus() == TThreadPool::BUSY) pServer->AddThreads();
if (pServer->GetThreadPoolStatus() == TThreadPool::IDLE) pServer->RemoveThreads();
goto LABEL_MANAGER_PROCESSING;
}
return (0);
}
工作者线程实现:
unsigned __stdcall TThreadPool::WorkerProc(void* p)
{
TThreadPool* pServer = (TThreadPool*) p;
HANDLE IoPort = pServer->GetWorkerIoPort();
unsigned long pN1, pN2;
OVERLAPPED* pOverLapped;
DWORD threadId = ::GetCurrentThreadId();
while (::GetQueuedCompletionStatus(IoPort, &pN1, &pN2, &pOverLapped, INFINITE))
{
if (pOverLapped == (OVERLAPPED*) 0xFFFFFFFE)
{
pServer->RemoveThread(threadId);
break;
}
else if (pOverLapped == (OVERLAPPED*) 0xFFFFFFFF)
{
break;
}
else
{
pServer->ChangeStatus(threadId, true);
pfnWork pWork = reinterpret_cast < pfnWork > (pN1);
unsigned uRet = pWork((void*) pN2);
pServer->ChangeStatus(threadId, false);
}
}
return (0);
}