我想知道哪种方法更快,为什么?
在编写 Win32 服务器时,我已经阅读了很多关于 Completion Ports 和 Overlapped I/O 的内容,但我没有阅读任何内容来建议哪组 API 在服务器中产生最佳结果。
我应该使用完成例程,还是应该使用 WaitForMultipleObjects API,为什么?
我想知道哪种方法更快,为什么?
在编写 Win32 服务器时,我已经阅读了很多关于 Completion Ports 和 Overlapped I/O 的内容,但我没有阅读任何内容来建议哪组 API 在服务器中产生最佳结果。
我应该使用完成例程,还是应该使用 WaitForMultipleObjects API,为什么?
您建议使用两种方法进行重叠 I/O 并忽略第三种方法(或者我误解了您的问题)。
当您发出重叠操作时,例如WSARecv(),您可以指定一个包含事件的 OVERLAPPED 结构,您可以等待该事件发出信号以指示重叠 I/O 已完成。我假设这是您的 WaitForMultipleObjects() 方法,并且如前所述,这不能很好地扩展,因为您受限于可以传递给 WaitForMultipleObjects() 的句柄数量。
或者,您可以传递一个完成例程,该例程在完成时调用。这称为“可警报 I/O”,并要求发出 WSARecv() 调用的线程处于“可警报”状态,才能调用完成例程。线程可以通过多种方式将自己置于警报状态(调用 SleepEx() 或等待函数的各种 EX 版本等)。我在我面前打开的Richter 书中说:“我曾多次使用可警报 I/O,我将第一个告诉您可警报 I/O 是可怕的,应该避免”。够了,恕我直言。
还有第三种方法,在发出调用之前,您应该将要在其上执行重叠 I/O 的句柄与完成端口相关联。然后,您通过调用 GetQueuedCompletionStatus() 和循环来创建一个线程池,为该完成端口提供服务。您使用 OVERLAPPED 结构发出 WSARecv() 而其中没有事件,并且当 I/O 完成时,完成从您的 I/O 池线程之一上的 GetQueuedCompletionStatus() 弹出并可以在那里处理。
如前所述,Vista/Server 2008 已经稍微清理了 IOCP 的工作方式,并消除了您必须确保发出重叠请求的线程继续运行直到请求完成的问题。可以在此处找到指向该参考的链接。但无论如何,这个问题很容易解决;您只需使用与完成相同的 IOCP 将 WSARecv 编组到您的 I/O 池线程之一...
无论如何,恕我直言,使用 IOCP 是进行重叠 I/O 的最佳方式。是的,在开始时了解调用的重叠/异步性质可能需要一些时间,但这是非常值得的,因为系统可以很好地扩展并提供一种简单的“即发即弃”方法来处理重叠操作。
如果你需要一些示例代码来帮助你开始,那么我有几篇关于编写 IO 完成端口系统的文章和一堆免费代码,它们为高性能服务器提供了一个真实的框架;看这里。
作为旁白; 恕我直言,您真的应该阅读 Jeffrey Richter 和 Christophe Nasarre 所著的“ Windows Via C/C++ (PRO-Developer) ”,因为它涉及您需要了解的有关重叠 I/O 和大多数其他高级 Windows 平台技术和 API 的所有信息。
WaitForMultipleObjects
仅限于 64 个句柄;在高度并发的应用程序中,这可能成为一个限制。
完成端口更适合具有线程池的模型,所有线程池都能够处理任何事件,您可以将自己的(非基于 IO 的)事件排队到端口中,而等待则需要编写自己的代码机制。
然而,完成端口和基于事件的编程模型是一个更难以真正解决的概念。
我预计不会有任何显着的性能差异,但最终您只能进行自己的测量以反映您的使用情况。请注意,Vista/Server2008 对完成端口进行了更改,即现在不需要原始线程来完成 IO 操作,这可能会产生更大的差异(请参阅 Mark Russinovich 的这篇文章)。
《Microsoft Windows 网络编程,第 2 版》一书中的表 6-3比较了通过完成端口与其他技术的重叠 I/O 的可伸缩性。在吞吐量方面,完成端口将所有其他 I/O 模型从水中淘汰,同时使用更少的线程。
WaitForMultipleObjects() 和 I/O 完成端口之间的区别在于 IOCP 可以扩展到数千个对象,而 WFMO() 不会也不应该用于超过 64 个对象(即使您可以)。
您无法真正比较它们的性能,因为在 < 64 个对象的域中,它们本质上是相同的。
然而,WFMO() 会对其对象进行循环,因此具有低索引号的繁忙对象可能会使具有高索引号的对象饿死。(例如,如果对象 0 不断关闭,它将使对象 1、2、3 等饿死)。这显然是不可取的。
我写了一个 IOCP 库(用于套接字)来解决 C10K 问题并将其放在公共域中。我能够在 512mb W2K 机器上获得 4,000 个套接字同时传输数据。(如果它们空闲,您可以获得更多的套接字 - 繁忙的套接字会消耗更多的非分页池,这是您可以拥有多少个套接字的最终限制)。
http://www.45mercystreet.com/computing/libiocp/index.html
API 应该为您提供您所需要的。
没有把握。但我使用 WaitForMultipleObjects 和/或 WaitFoSingleObjects。这很方便。
任何一种常规工作都有效,我真的不认为一个比另一个更快。
这两种方法的存在是为了满足不同的编程模型。WaitForMultipleObjects 用于促进异步完成模式(如 UNIX select() 函数),而完成端口更倾向于事件驱动模型。
我个人认为 WaitForMultipleObjects() 方法可以使代码更清晰,线程更安全。