I/O完成端口与RegisterWaitForSingleObject?

Meh*_*dad 3 file-io winapi asynchronous visual-c++ io-completion-ports

使用I/O完成端口与使用RegisterWaitForSingleObject线程池线程等待I/O完成之间有什么区别?

其中一个更快,如果是,为什么?

Who*_*aig 8

IOCP通常是执行速度最快的IO周转机制,您会找到其中一个最重要的原因:阻塞检测.

一个简单的例子是一个负责从磁盘提供文件的服务器.IOCP通常由三个主要部分组成:

  1. 用于为IOCP请求提供服务的N个线程池.
  2. M个线程的限制(M总是<N)告诉IOCP 允许多少并发,非阻塞的线程.
  3. 所有线程都运行的完成状态循环.

N和M之间的差异非常重要.一般的理念是将M配置为机器上的核心数,将N配置为更大.多大程度取决于工作线程在阻塞状态下花费的时间.如果您正在读取磁盘文件,则您的线程将受到磁盘IO通道的速度限制.当你拨打电话时,ReadFile()你刚刚推出了阻止电话.如果M == N,那么只要您点击所有线程读取磁盘文件,就会完全停止,磁盘IO通道上的所有线程都会停止.

但是,如果某个花哨的调度程序有一种方法可以"知道"该线程是(a)参与IOCP线程池,并且(b)因为它发出了一个耗时的API调用而停滞不前,那该怎么办呢?如果,当发生这种情况时,花哨的调度程序可以暂时将该线程"移动"到一个特殊的"运行但停滞"的组中,然后"释放"一个额外的线程,该线程在线程停止时自愿工作?

正是 IOCP带来的.当N 大于 M时,IOCP会将刚刚发出停顿的线程置于一个特殊的运行但停止状态,然后暂时从你的N池中"借"一个额外的线程.它将继续这样做直到N池已耗尽,或者已停止的线程开始从阻塞请求返回.

因此,在这种情况下,配置为具有8个并发运行在8核机器上的8个线程的IOCP实际上可以在实际池中具有几百个线程.只有8个将被"允许"同时在非阻塞状态下运行,但是当被阻塞的线程从它们的块返回并且你已经借用线程服务于其他请求时,你可能暂时弹出它.

最后,尽管对您的原因不那么重要,但仍然很重要:如果队列在完成当前工作并发出下一个GetQueueCompletionStatus()调用时队列中有待处理的工作,则IOCP线程将不会阻塞,也不会进行上下文切换.如果有工作在等待,它将接收它并继续执行而没有强制抢占.当然,操作系统调度程序无论如何都可以抢占,但仅作为一般调度程序的一部分; 不是因为具体的要求GetQueueCompletionStatus().唯一的例外是,如果已经有超过M个线程在运行且没有阻塞.在这种情况下,GetQueueCompletionStatus()当有足够的线程再次被阻塞时,将阻塞调用线程直到再次需要松弛工作.

您给出的描述表明您将受到严重的磁盘绑定.对于绝对性能关键的io-server体系结构,几乎不可能超越IOCP的优势,尤其是操作系统级别的块检测,它允许调度程序知道它可以暂时从主池中释放额外的线程来保存事物在其他线程停止时抽水.

您无法使用Windows线程池复制IOCP的特定功能.如果你的所有线程都是数字处理器,很少或没有IO,我会说线程池更合适,但是你对disk-IO的特殊性告诉我你应该使用IOCP.

  • 除此之外还有IOCP使用不公平的调度(睡眠的线程往往会保持睡眠状态,正在运行的线程往往会留在内存中),这意味着您可以使N非常大,而不必担心抖动调度程序和内存管理器. (2认同)