日期:2014-05-17  浏览次数:20856 次

IOCP加Windows线程池打造高伸缩性高性能的服务器应用

       对于IOCP,搞Windows服务器编程的都不会陌生,它所表现出来的性能是其他各种WinSock模型难望其项背的。撰写本文的目的就是为让大家能够在深入理解IOCP的基础上,再来深入的挖掘Windows系统的性能。此处假设读者对IOCP模型已经深刻理解,并对Windows线程、线程池有一定的了解。如果对此还不熟悉,限于篇幅的原因,请您先学习理解这些内容后再来阅读本文。
       在IOCP模型编程中,我们经常需要考虑的就是创建多少个线程来作为完成执行线程,很多时候这是个非常需要技巧和经验的决策性问题。大多数情况下,我们采取的策略是看服务器上有多少CPU然后假定每个CPU最多执行两个线程,然后我们创建的线程数量就是CPU数*2。这看起来很合理,但是实际上在复杂的服务器应用环境中,这样做的效果并不尽如人意,很多时候我们希望得到一种更加动态灵活的方案。
       有些有经验的程序员就自己编写线程池库,来实现这种动态灵活的管理方式,从而还可以实现一定的扩展性,比如系统动态的添加了一些CPU的资源,或者系统负担比较重的时候,或者CPU因为频繁切换线程场景而导致效率低下时,线程池的动态性就发挥出来了。

       索性的是,在Windows2000以上的平台上,已经为我们提供了线程池的接口,虽然这些接口有时候看起来还有些简陋,比如有名的QueueUserWorkItem函数,这些接口简陋到你连当前线程池中有多少活动线程等信息都无法知道,你只能通过其它的工具来动态观察和猜测。但这样的简单性也为我们带来了调用方便的实惠。当然到了Windows2008以上的平台时,线程池的函数总算是被大大加强了,你可以控制更多东西了,关于Windows2008线程池的内容请看我的另一篇博客拙作《Windows2008线程池前瞻》。
       在结合IOCP和线程池这方面Windows系统也想到了程序员面临的这个困难,Windows系统干脆直接就在系统内部捆绑了IOCP和线程池,提供了一个带IOCP功能的线程池函数——BindIoCompletionCallback。
此函数的原形如下:
BOOL WINAPI BindIoCompletionCallback(
  __in          HANDLE FileHandle,
  __in          LPOVERLAPPED_COMPLETION_ROUTINE Function,
  ULONG Flags
);

需要的完成过程(实际也就是IOCP线程的过程)Function的原形需要你定义成如下的样子:
VOID CALLBACK FileIOCompletionRoutine(
  [in]                 DWORD dwErrorCode,
  [in]                 DWORD dwNumberOfBytesTransfered,
  [in]                 LPOVERLAPPED lpOverlapped
);

        熟悉IOCP的各位可能已经兴奋得血管暴胀了吧?
       从BindIoCompletionCallback函数的参数你应该已经能够猜到这个函数的用法了,第一个句柄就是你需要捆绑的文件句柄或者SOCKET套接字句柄,甚至是其他I/O设备的句柄。第二个函数的指针就是你的完成例程的指针,这个函数完全由你实现和控制,最后一个Flags参数当前所有的Windows系统中都必须赋予0值,这个参数实际上还没有被起用。
       这么简单?真是难以置信,代表IOCP的句柄上哪去了?其实哪个什么IOCP的句柄,以及创建多少个线程什么的都不需要你考虑了,你唯一需要操心的就是如何编写完成例程以及如何将一个I/O句柄和完成例程捆绑起来。以前需要n多行代码才能完成的事情,一个BindIoCompletionCallback函数就彻底搞定了,甚至我们不需要再考虑线程的动态性问题了。这一切现在都有Windows系统综合考虑了,而你就被解放出来了。
还愣着干嘛?快去写你的高可用性,高可扩展、高动态性的IOCP大型服务应用去了!