I / O完成端口与RegisterWaitForSingleObject?

时间:2013-04-26 18:46:06

标签: winapi visual-c++ file-io asynchronous io-completion-ports

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

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

1 个答案:

答案 0 :(得分:8)

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

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

  1. 用于为IOCP请求提供服务的N个线程池。
  2. M个线程的限制(M总是并发,非阻塞线程。
  3. 所有线程都在运行的完成状态循环。
  4. N和M之间的区别在于非常重要。一般的理念是将M配置为机器上的核心数,将N配置为更大。多大程度取决于工作线程在阻塞状态下花费的时间。如果您正在读取磁盘文件,则您的线程将受到磁盘IO通道的速度限制。当您拨打ReadFile()时,您刚刚引入了阻止呼叫。如果M == N,那么只要您点击所有读取磁盘文件的线程,就会完全停止,磁盘IO通道上的所有线程都会停止。

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

    正是 IOCP带来的东西。当N 大于 时,IOCP会将刚刚发出停顿的线程置于一个特殊的运行但停止状态,然后暂时从你的N池中“借”一个额外的线程。它将继续执行此操作,直到N池耗尽,或者停止的线程开始从阻塞请求返回。

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

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

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

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