现在的位置: 首页 > 综合 > 正文

共享Windows下C++库之线程池篇

2012年01月17日 ⁄ 综合 ⁄ 共 1692字 ⁄ 字号 评论关闭

 

上一篇我把创建单个线程的源码给出来了,当然,线程池的也不能拉下~

 

很多时候,我们需要一组线程来解决问题。当然可以创建一些线程来完成工作,然后关闭掉。当又需要时,重复上述过程即可。然而,无论是客户端还是服务端,完全没必要如此,只需要实现创建一组线程,按需分配,不必创建--关闭--创建--关闭…

 

也许我们也能自己写成符合需求的线程池组件,但是,系统也提供了线程池组件。我认为就应该重复利用(出于学习的目的另当别论)。在这里,主要针对XP系统,提供一组C++ wrap过的线程池组件和模仿ATL线程池。当然,使用系统的线程池有一定的限制(设置线程堆栈大小等)。但还是能满足大部分的需求。

 

首先,来看看系统线程池(据《Windows核心编程》所说,在内部都是用完成端口来管理的)

Windows 线程池中的线程有两种类型,一种可以用来处理异步I/O, 另一种则不能。前者依赖于IO完成端口,IOCP是一种Windows内核对象,它可以将线程和I/O端口绑定在特定的系统资源上,对带有完成端口的I/O进行处理是一个复杂的过程。

 

  1. QueueUserWorkItemPool--Windows将创建一个线程池,其中的一个线程将执行 回调函数,函数执行完成后,该线程返回线程池,等待新的任务。当然了,你不能在该线程的回调函数中执行ExitThread等破坏行为。线程池中的线程数量是动态的,Windows内部的调度算法决定当前线程工作负载的最佳方式。如果要执行长时间的处理工作,需要设置WT_EXECUTELONGFUNCTION。如果该线程不执行异步IO,则设置WT_EXECUTIONDEFAULT即可,如果需要执行异步IO,则应该设置WT_EXECUTEIONIOTHREAD标志来告诉线程池
  2. BindIOCompletionCallbackPool--服务器应用程序发出某些异步IO请求,当这些请求完成时,需要让一个线程池准备好来处理已完成的IO请求。BindIoCompletionCallback在内部调用CreateIoCompletionPort,传递hDevice和内部完成端口的句柄。调用该函数可以保证至少有一个线程始终在非IO组件中,与设备相关的完成键是重叠完成例程的地址。
    这样,当该设备的IO运行完成时,非IO组件就知道调用哪个函数,以便能够处理已完成IO请求
  3. RegisterWaitForSingleObjectPool--线程池在等待一个事件,当该事件被激活时,会唤醒一个线程来执行注册的回调函数。

 

其次,来看如何使用,简单展示下QueueUserWorkItemPool,其他的则在Example中给出

class A
{
enum { Count = 1000000 };
public:
void DoWork()
{
LONG lVal = 0;
for(DWORD i = 0; i != Count; ++i)
{
InterlockedExchangeAdd(&lVal, i);
}

}

void DoWork(int nNum)
{
LONG lVal = 0;
for(DWORD i = 0; i != nNum; ++i)
{
InterlockedExchangeAdd(&lVal, i);
}
}
};

A a;
QueueUserWorkItemPool::Call(&A::DoWork, &a);
QueueUserWorkItemPool::CallEx(&A::DoWork, &a, nCount);

很简单,是吧,只需要设置回调函数,CallEx比Call多提供一个额外参数,当然可以是任意类型.

然而,在对于BindIOCompletionCallbackPool的设计时,为了区分每个不同的回调函数,把自己弄成了模板类,需要提供一个额外的模板参数来进行区分--

BindIOCompletionCallbackPool<1>::CallBindIOCompletionCallbackPool<2>::Call

 

如果你有更好的建议,希望你能提出,大家共同提高。

 

所有的源码和示例以提供:(需要更改后缀,把.gif改为.rar,用右键下载)

猛击这里 猛击这里

【上篇】
【下篇】

抱歉!评论已关闭.