刚翻译一篇MSDN的文章,关于完成端口,也许其他人已经翻译过了,不过我自己再翻译一遍,希望摸得更准一点。
========================================================
I/O Completion Ports
I/O完成端口
I/O completion ports provide an efficient threading model for processing multiple asynchronous I/O requests on a multiprocessor system. When a process creates an I/O completion port, the system creates an associated queue object for requests whose sole purpose is to service these requests. Processes that handle many concurrent asynchronous I/O requests can do so more quickly and efficiently by using I/O completion ports in conjunction with a pre-allocated thread pool than by creating threads at the time they receive an I/O request.
I/O完成端口为多处理器系统上处理多种异步请求提供了一个有效的线程模型。当进程创建一个I/O完成端口时,系统会为这些请求创建一个关联的队列对象,此对象只服务于这些请求。相比在接收到I/O请求时创建线程,同时使用I/O完成端口和预分配的线程池,处理大并发异步I/O请求的进程可以更加快速高效。
How I/O Completion Ports Work
I/O完成端口如何工作
The CreateIoCompletionPort function creates an I/O completion port and associates one or more file handles with that port. When an asynchronous I/O operation on one of these file handles completes, an I/O completion packet is queued in first-in-first-out (FIFO) order to the associated I/O completion port. One powerful use for this mechanism is to combine the synchronization point for multiple file handles into a single object, although there are also other useful applications. Please note that while the packets are queued in FIFO order they may be dequeued in a different order.
CreateIoCompletionPort函数可以创建一个完成端口,并且可以在此端口上关联一个或多个文件句柄。当这些文件句柄中的某个句柄上的一个异步I/O操作完成时,一个I/O完成包会按先进先出顺序加入关联的I/O完成端口队列。这种机制的一种强大应用是把多个文件句柄的同步点放入一个对象,当然也有其他有用的用法。请注意当包按先进先出顺序加入队列时,它们可能按不同的顺序被取出队列。
Note
注意
The term file handleas used here refers to a system abstraction representing an overlapped I/O endpoint, not only a file on disk. For example, it can be a network endpoint, TCP socket, named pipe, or mail slot. Any system object that supports overlapped I/O can be used. For a list of related I/O functions, see the end of this topic.
这里所使用的术语“file handles”指的是一个系统抽象概念,表示一个重叠I/O终结点,而不仅仅指磁盘上的一个文件。例如,它可以是一个网络终结点,TCP套接字,命名管道,或者邮件槽。任何支持重叠I/O的系统对象都可以在这里使用。要查看相关I/O函数列表,请转到此话题结尾。
When a file handle is associated with a completion port, the status block passed in will not be updated until the packet is removed from the completion port. The only exception is if the original operation returns synchronously with an error. A thread (either one created by the main thread or the main thread itself) uses the GetQueuedCompletionStatus function to wait for a completion packet to be queued to the I/O completion port, rather than waiting directly for the asynchronous I/O to complete. Threads that block their execution on an I/O completion port are released in last-in-first-out (LIFO) order, and the next completion packet is pulled from the I/O completion port's FIFO queue for that thread. This means that, when a completion packet is released to a thread, the system releases the last (most recent) thread associated with that port, passing it the completion information for the oldest I/O completion.
当一个文件句柄被关联到一个完成端口,输入的状态信息结构只有在I/O完成包从完成端口队列中移除时才会更新。仅有的异常是否原始操作会出现错误并同步返回。一个线程(主线程或主线程创建的线程)可以使用GetQueuedCompletionStatus函数来等待一个完成包体加入到I/O完成端口队列,而不是直接等待同步I/O操作的完成。在一个I/O完成端口上阻塞的线程会按后进先出顺序释放,并从I/O完成端口的先进先出队列中取出下一个完成包。这意味着,当一个完成包被释放给一个线程,系统会释放关联到此端口上的最后(最近)一个线程,并传递最老的一个I/O完成信息给它。
Although any number of threads can call GetQueuedCompletionStatus for a specified I/O completion port, when a specified thread calls GetQueuedCompletionStatusthe first time, it becomes associated with the specified I/O completion port until one of three things occurs: The thread exits, specifies a different I/O completion port, or closes the I/O completion port. In other words, a single thread can be associated with, at most, one I/O completion port.
虽然任何数量的线程都可以在一个指定的I/O完成端口上调用GetQueuedCompletionStatus,但当一个指定的线程第一次调用GetQueuedCompletionStatus后,它会一直关联到指定的I/O完成端口直到出现三种情况:线程退出,指定另一个I/O完成端口,或者关闭I/O完成端口。换句话说,一个线程最多只能被关联到一个I/O完成端口。
When a completion packet is queued to an I/O completion port, the system first checks how many threads associated with that port are running. If the number of threads running is less than the concurrency value (discussed in the next section), one of the waiting threads (the most recent one) is allowed to process the completion packet. When a running thread completes its processing, it typically calls GetQueuedCompletionStatus again, at which point it either returns with the next completion packet or waits if the queue is empty.
当一个完成包被加入I/O完成端口的队列,系统首先检查关联到此端口的线程有多少正在运行。如果线程数量比并发值少(在下一章讨论),其中一个等待的线程(最近一个)将被允许处理此完成包。当一个运行中的线程完成它的处理过程,正常情况下它会再次调用GetQueuedCompletionStatus,这时它会返回下一个完成包,如果队列是空的则等待。
Threads can use the PostQueuedCompletionStatus function to place completion packets in an I/O completion port's queue. By doing so, the completion port can be used to receive communications from other threads of the process, in addition to receiving I/O completion packets from the I/O system. The PostQueuedCompletionStatusfunction allows an application to queue its own special-purpose completion packets to the I/O completion port without starting an asynchronous I/O operation. This is useful for notifying worker threads of external events, for example.
线程可以使用PostQueuedCompletionStatus函数来将一个完成包放入I/O完成端口的队列。这样做,完成端口可以用于接收进程其他线程的通信,而不仅仅用于接收I/O系统的I/O完成包。PostQueuedCompletionStatus函数允许一个应用程序将它自己特定目的的完成包加入完成端口队列,而不用使用一个异步I/O操作。其实这在通知工作线程处理外部事件时很有用。
The I/O completion port handle and every file handle associated with that particular I/O completion port are known as references to the I/O completion port. The I/O completion port is released when there are no more references to it. Therefore, all of these handles must be properly closed to release the I/O completion port and its associated system resources. After these conditions are satisfied, an application should close the I/O completion port handle by calling the CloseHandle function.
I/O完成端口句柄和每个关联到此特定I/O完成端口的文件句柄被看作对I/O完成端口的引用。在没有引用时I/O完成端口才会被释放。因此,所有这些句柄必须恰当地关闭,这样才能释放I/O完成端口和它关联的系统资源。当这些情况满足后,应用程序应当调用CloseHandle函数关闭I/O完成端口句柄。
Note
注意
An I/O completion port is associated with the process that created it and is not sharable between processes. However, a single handle is sharable between threads in the same process.
一个I/O完成端口会关联到创建它的进程,但并不能在进程间共享。当然,在同一个进程的线程间单个句柄是可以共享的。
Threads and Concurrency
线程和并发
The most important property of an I/O completion port to consider carefully is the concurrency value. The concurrency value of a completion port is specified when it is created withCreateIoCompletionPort via the NumberOfConcurrentThreadsparameter. This value limits the number of runnable threads associated with the completion port. When the total number of runnable threads associated with the completion port reaches the concurrencyvalue, the system blocks the execution of any subsequent threads associated with that completion port until the number of runnable threads drops below the concurrency value.
需要考虑的I/O完成端口最重要的属性是并发值。一个完成端口的并发值在它被CreateIoCompletionPort创建时通过NumberOfConcurrentThreads这个参数指定。这个值限定了关联到此完成端口的可运行线程数量。当关联到此完成端口的可运行线程总数达到并发值,系统阻塞关联到此完成端口的任何后续线程的执行,直到可运行线程数量降到并发值以下。
The most efficient scenario occurs when there are completion packets waiting in the queue, but no waits can be satisfied because the port has reached its concurrency limit. Consider what happens with a concurrency value of one and multiple threads waiting in the GetQueuedCompletionStatus function call. In this case, if the queue always has completion packets waiting, when the running thread callsGetQueuedCompletionStatus, it will not block execution because, as mentioned earlier, the thread queue is LIFO. Instead, this thread will immediately pick up the next queued completion packet. No thread context switches will occur, because the running thread is continually picking up completion packets and the other threads are unable to run.
最高效的场景出现在,队列中有完成包等待处理,但因为端口已经达到它的并发上限无法满足等待条件。想一想并发值这种数量的一个或多个线程正在等待调用GetQueuedCompletionStatus函数,会发生什么。在这种情况下,如果队列中一直有完成包待处理,当正在运行的线程调用GetQueuedCompletionStatus,它将不会阻塞运行,因为在上面说过,线程队列是后进先出的。相反,这个线程将会立即获得队列中下一个完成包。没有线程上下文件切换的情况发生,因为运行中的线程会持续地获得完成包,而其他线程无法运行。
Note
注意
In the previous example, the extra threads appear to be useless and never run, but that assumes that the running thread never gets put in a wait state by some other mechanism, terminates, or otherwise closes its associated I/O completion port. Consider all such thread execution ramifications when designing the application.
在前面的例子中,额外的线程好像看起来没什么用,并且一直没有运行,但这是假定运行中的线程从没有被一些其他机制置为等待状态,终止,或者关闭它关联的I/O完成端口。当设计程序时应当考虑所有这些线程执行流程。
The best overall maximum value to pick for the concurrency value is the number of CPUs on the computer. If your transaction required a lengthy computation, a larger concurrency value will allow more threads to run. Each completion packet may take longer to finish, but more completion packets will be processed at the same time. You can experiment with the concurrency value in conjunction with profiling tools to achieve the best effect for your application.
作为并发值的最佳的总体最大值是计算机中央处理器的数量。如果你的事务需要长时间运算,设置一个更大的并发值可以允许更多的线程运行。每一个完成包或许需要长时间来完成,但更多的完成包会在同时处理。你可以结合性能分析工具来为你的程序试验出一个最高效的并发值。
The system also allows a thread waiting in GetQueuedCompletionStatus to process a completion packet if another running thread associated with the same I/O completion port enters a wait state for other reasons, for example the SuspendThread function. When the thread in the wait state begins running again, there may be a brief period when the number of active threads exceeds the concurrency value. However, the system quickly reduces this number by not allowing any new active threads until the number of active threads falls below the concurrency value. This is one reason to have your application create more threads in its thread pool than the concurrency value. Thread pool management is beyond the scope of this topic, but a good rule of thumb is to have a minimum of twice as many threads in the thread pool as there are processors on the system. For additional information about thread pooling, see Thread Pools.
系统也允许一个线程等待GetQueuedCompletionStatus来处理一个完成包,而另一个关联到同一个I/O完成端口的运行线程因为其他原因进入一个等待状态,比如SuspendThread函数。当处在等待状态的线程重新开始运行,可能会有一小段时间,活动线程数量超过并发值。当然,系统会通过禁用新的活动线程迅速减少这个数值,直到活动线程数量降到并发值以下。这是一个导致你的程序在它的线程池中创建比并发值更多线程的原因。线程池管理已经超出此话题的范围,但有一个好的经验法则是在线程池中创建至少系统处理器两倍数量的的线程。要获取更多关于线程池的信息,请查看Thread Pools。
Supported I/O Functions
支持的I/O函数
The following functions can be used to start I/O operations that complete by using I/O completion ports. You must pass the function an instance of the OVERLAPPED structure and a file handle previously associated with an I/O completion port (by a call to CreateIoCompletionPort) to enable the I/O completion port mechanism:
以下函数可以被用于启动一个使用I/O完成端口完成的I/O操作。你可以给函数传递一个OVERLAPPED结构的实例和一个已经提前关联到一个I/O完成端口的文件句柄(通过调用CreateIoCompletionPort)来启用I/O完成端口机制:
· ConnectNamedPipe
· DeviceIoControl
· LockFileEx
· ReadDirectoryChangesW
· ReadFile
· TransactNamedPipe
· WaitCommEvent
· WriteFile
· WSASendMsg
· WSASendTo
· WSASend
· WSARecvFrom
· WSARecvMsg
· WSARecv
Related topics
相关话题
About Processes and Threads
BindIoCompletionCallback
CreateIoCompletionPort
GetQueuedCompletionStatus
GetQueuedCompletionStatusEx
PostQueuedCompletionStatus