完成端口加线程池技术实现

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

WinSock 异步I/O模型[5]---完成端口 - Completion Port

----------------------------------------------------------------------------

如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。Windows操作系统提供了五种I/O模型,分别是:

■选择(select);

■异步选择(WSAAsyncSelect);

■事件选择(WSAEventSelect);

■重叠I/O(Overlapped I/O);

■完成端口(Completion Port) 。

每一种模型适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

==============================================

█“完成端口”模型是迄今为止最复杂的一种 I/O 模型。但是,若一个应用程序同时需要管理很多的套接字,

那么采用这种模型,往往可以达到最佳的系统性能!但缺点是,该模型只适用于Windows NT 和 Windows 2000 以上版本的操作系统。

█因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,

应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。

█从本质上说,完成端口模型要求我们创建一个 Win32 完成端口对象,通过指定数量的线程,

对重叠 I/O 请求进行管理,以便为已经完成的重叠 I/O 请求提供服务。

█※※※大家可以这样理解,一个完成端口其实就是一个完成 I/O 的通知队列,由操作系统把已经完成的重叠 I/O 请求的通知放入这个队列中。

当某项 I/O 操作一旦完成,某个可以对该操作结果进行处理的工作者线程就会收到一则通知,工作者线程再去做一些其他的善后工作,

比如:将收到的数据进行显示,等等。而套接字在被创建后,可以在任何时候与某个完成端口进行关联。※※※

通常情况下,我们会在应用程序中创建一定数量的工作者线程来处理这些通知。线程数量取决于应用程序的特定需要。理想的情况是,线程数量等于处理器的数量,不过这也要求任何线程都不应该执行诸如同步读写、等待事件通知等阻塞型的操作,以免线程阻塞。每个线程都将分到一定的CPU时间,在此期间该线程可以运行,然后另一个线程将分到一个时间片并开始执行。如果某个线程执行了阻塞型的操作,操作系统将剥夺其未使用的剩余时间片并让其它线程开始执行。也就是说,前一个线程没有充分使用其时间片,当发生这样的情况时,应用程序应该准备其它线程来充分利用这些时间片。

█使用这种模型之前,首先要创建一个 I/O 完成端口对象,用它面向任意数量的套接字句柄,管理多个 I/O 请求。

要做到这一点,需要调用 CreateCompletionPort 函数,其定义如下:

HANDLE WINAPI CreateIoCompletionPort(

__in HANDLE FileHandle,

__in HANDLE ExistingCompletionPort,

__in ULONG_PTR CompletionKey,

__in DWORD NumberOfConcurrentThreads

);

要注意该函数有两个功能:

●用于创建一个完成端口对象;

●将一个句柄同完成端口对象关联到一起。

如果仅仅为了创建一个完成端口对象,唯一注意的参数便是NumberOfConcurrentThreads(并发线程的数量),前面三个参数可忽略。

NumberOfConcurrentThreads 参数的特殊之处在于,它定义了在一个完成端口上,同时允许执行的线程数量。

理想情况下,我们希望每个处理器各自负责一个线程的运行,为完成端口提供服务,避免过于频繁的线程“场景”(即线程上下文)切换。

若将该参数设为 0,表明系统内安装了多少个处理器,便允许同时运行多少个工作者线程!可用下述代码创建一个 I/O 完成端口:

HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

★1、工作者线程与完成端口

成功创建一个完成端口后,便可开始将套接字句柄与其关联到一起。但在关联套接字之前,首先必须创建一个或多个“工作者线程”,

以便在 I/O 请求投递给完成端口后,为完成端口提供服务。在这个时候,大家或许会觉得奇怪,到底应创建多少个线程,以便为完成端口提供服务呢?

在此,要记住的一点,我们调用 CreateIoComletionPort 时指定的并发线程数量,与打算创建的工作者线程数量相比,它们代表的不是同一件事情。

CreateIoCompletionPort 函数的 NumberOfConcurrentThreads 参数明确指示系统:

在一个完成端口上,一次只允许 n 个工作者线程运行。假如在完成端口上创建的工作者线程数量超出 n 个,那么在同一时刻,最多只允许n个线程运行。

但实际上,在一段较短的时间内,系统有可能超过这个值,但很快便会把它减少至事先在 CreateIoCompletionPort 函数中设定的值。

那么,为何实际创建的工作者线程数量有时要比 CreateIoCompletionPort 函数设定的

多一些呢?这样做有必要吗?

这主要取决于应用程序的总体设计情况。假定我们的某个工作者线程调用了一个函数,比如 Sleep 或 WaitForSingleObject,

进入了暂停(锁定或挂起)状态,那么允许另一个线程代替它的位置。换言之,我们

希望随时都能执行尽可能多的线程;

当然,最大的线程数量是事先在CreateIoCompletonPort 调用里设定好的。这样一来,假如事先预计到自己的线程有可能暂时处于停顿状态,

那么最好能够创建比 CreateIoCompletonPort 的 NumberOfConcurrentThreads 参数的值多的线程,以便到时候充分发挥系统的潜力。

==========================================

每一种模型适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,

综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

==============================================

█“完成端口”模型是迄今为止最复杂的一种 I/O 模型。但是,若一个应用程序同时

需要管理很多的套接字,

那么采用这种模型,往往可以达到最佳的系统性能!但缺点是,该模型只适用于Windows NT 和 Windows 2000 以上版本的操作系统。

█因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,

应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。

█从本质上说,完成端口模型要求我们创建一个 Win32 完成端口对象,通过指定数量的线程,

相关文档
最新文档