从MSDN中翻译了IO完成端口的文章,不得不说翻译的很烂,英语需要继续提高啊。。。
在一个多处理器系统上,IO完成端口提供一个非常高效的线程模型来处理多个异步IO请求。当一个进程创建了一个IO完成端口,系统会创建一个关联的队列用于处理请求。进程处理大量并发异步IO请求是非常快速和高效的,通过使用IO完成端口结合一个线程池是比在接收到请求时创建线程高效的。
完成端口是如何工作的
CreateIoCompletionPort方法创建一个IO完成端口,并且关联一个或多个文件句柄。当这些文件的一个异步IO操作完成时,一个IO完成包会被入队到一个FIFO的关联这个IO完成端口的队列。
Note
这个地方引用的术语文件句柄代表一个重叠IO终结点,而不仅仅是磁盘上的文件。比如,它可以是一个网络终结点,TCP socket,命名管道,或者邮件槽。它可以是任何一个支持重叠IO的内核对象。
当一个文件句柄关联了一个完成端口,the status block passed in will not be updated until the packet is removed from the completion port。只有当原来的操作被同步的返回一个错误才会出现异常。一个线程(被主线程创建或者主线程自己)使用GetQueuedCompletionStatus方法等待一个完成数据包被入队到IO完成端口,而不是直接等待异步IO完成。线程阻塞他们的执行在一个IO完成端口按照LIFO的顺序被释放,并且对于那个线程来说下一个来自IO完成端口的FIFO队列的完成数据包被pulled。意思就是,当一个完成数据包被释放到一个线程,系统会释放最近的关联了完成端口的线程,把最早的IO完成信息传递给这个线程。
虽然对于一个特定的IO完成端口,任意数量的线程可以调用GetQueuedCompletionStatus,当一个特定的线程第一次调用GetQueuedCompletionStatus,它开始关联这个置顶的IO完成端口知道下面三个事情发生:线程退出,指定一个不同的IO完成端口,或者关闭这个完成端口。此外一个线程最多关联一个完成端口。
当一个完成数据包入队到一个完成端口中,系统首先检查有多少个关联的线程正在运行。如果运行的线程的数量小于并发值,一个等待的线程被允许处理这个数据包。当一个运行的线程完成了它的处理,它又会调用GetQueuedCompletionStatus,它要么处理下一个数据包,要么当队列是空的时候进行等待。
线程可以使用PostQueuedCompletionStatus是一个完成数据包进入一个IO完成端口的队列。这样做,完成端口就可以被用来与其他线程进行通信,此外,还接收来自IO系统的完成数据包,PostQueuedCompletionStatus方法允许一个程序在不启动一个异步IO操作的情况下,入队它自己指定意图的完成数据包到这个完成端口。用于通知工作线程外部事件是非常有用的。
当没有引用时,完成端口会被释放。因此所有的句柄必须被适当的关闭,来释放完成端口和它关联的系统资源。程序应该使用CloseHandle来关闭完成端口句柄。
线程和并行
线程的并行值是完成端口需要考虑的最重要的属性。并行值是完成端口创建时通过NumberOfConcurrentThreats参数指定的。这个值限制了关联这个完成端口的可以运行的线程的数量。当所有可运行的线程达到最大值,系统会阻塞任何后来的线程的执行,直到可运行的线程数比并行值要小。
并行值的最大值最好是CPU的数量。如果你的事务处理需要一个比较长的计算时间,一个大的并行值来让整个线程执行。每个数据包可能需要执行比较长的时间才能完成,但是同一时间可以有更多的数据包被处理。你可以通过profiling工具尝试并行值,来达到最好的效果。
由于有可能出现线程挂起的情况,所以程序要创建比并行值多的线程。一个比较好的规则是并行值的最小值是处理器的两倍。