完成端口在MSDN中的专题翻译


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.
IO完成端口在多处理器系统上提供一个高效的线程模型来处理并发异步IO请求。当一个进程创建一个IO完成端口后,系统会创建一个与之关联的队列对象来专门为这些请求服务。进程在预先分配的线程池的帮助下,会快速高效的处理并发的异步请求,这比来一个请求再去创建一个线程好多了。

How I/O Completion Ports Work 完成端口怎么工作
The CreateIoCompletionPort function Creates an IO completion port and associates one or more file handles with that port.When an asynchronous IO operation on one of these file handles completes, an IO completion packet is queued in first-in-first out(FIFO) order to the associated IO 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.
CreateIoCompletionPort函数创建一个完成端口并将多个文件句柄与之关联。当某个句柄上的一个异步IO操作完成,一个IO完成包以先进先出的顺序放到关联的完成端口里。这样的一个明显的好处是将多个文件句柄的异步操作合并到一个对象上,当然还有一些其它有用的好处。

Note
The term file handle as used here refers to system abstraction representing an overlapped IO 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 IO can be used.For a list of related IO functions, see the end of this topic.
注意
这里的文件句柄表示的是代表重叠IO类型的一个抽象,并不仅仅是磁盘上的文件。例如,还可以是网络端点,TCP socket,命名管道,或者邮槽。任何支持重叠IO的系统对象都可以用。相关的IO函数,可以参看这个主题的尾部。


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 IO completion port, rather than waiting directly for the asynchronous IO to complete.Threads that block their execution on an IO completion port are released in last-in-first-out(LIFO) order, and the next completion packet is pulled from the IO 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 IO completion.
一个线程(被主线程创建的或主线程本身)使用GetQueuedCompletionStatus函数等待一个完成包插入到完成端口里,而不是直接等待异步IO的完成。被完成端口阻塞的线程采取后进先出的顺序释放,下一个完成包被从完成端口的FIFO队列中提取出来给刚释放的线程。这也就是说,当一个完成包被解压给一个线程时,系统释放最近与完成端口关联的线程,给它传递等待最久的完成包。

Although any number of threads can call GetQueuedCompletionStatus for a specified IO completion port, when a specified thread calls GetQueuedCompletionStatus the first time, it becomes associated with the specified IO completion port until one of three things occurs: The thread exits, specifies a different IO completion port, or closes the IO completion port.In other words, a single thread can be associated with, at most, one IO completion port.
尽管任意线程可以调用GetQueuedCompletionStatus来查询一个特定的IO完成端口,但一个特定的线程第一次调用GetQueuedCompletionStatus时,这个线程就会与特定的完成端口关联直到下面三件事之一发生:线程退出,标识一个不同的IO完成端口,或者关闭IO完成端口,换句话说,一个单一的线程只能也最多与一个IO完成端口关联。

When a completion packet is queued to an IO 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.
当一个完成包被插入到完成端口里,系统首先检查多少个与这个完成端口关联的线程正在运行。如果运行线程的数量小于并发值(在下节讨论), 一个等待的线程(最晚被置为等待状态)被允许去处理完成包。当一个运行的线程完成它的处理时,它照例再一次调用GetQueuedCompletionStatus,此时线程或者得到下一个完成包或者一直等待如果队列是空的话。

Threads can use the PostQueuedCompletionStaus function to place completion packets in an IO 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 IO completion packets from the IO system.The PostQueuedCompletionStatus function allows an application to queue its own special-purpose completion packets to the IO completion port without starting an asynchronous IO operation.This is useful for notifying worker threads of external events, for example.
线程可以用PostQueuedCompletionStatus函数来放置完成包到一个IO完成端口的队列中。通过这样做,除了可以从IO系统中接收IO完成包外,完成端口还可以被用于接收进程中其它线程的消息。PostQueuedCompletionStatus函数允许一个程序来投递它自己的有特别用途的完成包到IO系统而不需要启动一个异步IO操作。这样很有用,例如,可以用于通知工作线程额外的事件。

The IO completion port handle and every file handle associated with that particular IO completion port are known as references to the IO completion port.The IO completion port is released when there are no more references to it.Therefore, all of these handles must be properly closed to release the IO 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.(这段不用翻译了,就是关闭句柄,前篇翻译过了。)

Threads and Concurrency
The most important property of an IO completion port to consider carefully is the concurrency value.The concurrency value of a completion port is specified when it is created with CreateIoCompletionPort via the NumberOfConcurrentThreads parameter.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 concurrency value, 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.
线程和并发
仔细斟酌,会发现并发值是完成端口最重要的属性。完成端口的并发值在调用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 on an multiple threads waiting in the GetQueuedCompletionStatus function calls.In this case, if the queue always has completion packets waiting, when the running thread calls GetQueuedCompletionStatus, 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 IO completion port.Consider all such thread execution ramifications when desining the application.
注意
在前面的例子中,额外的线程看起来没什么用,一直不能运行,但这样的前提是运行的线程永远不会等待,终止,或者用别的方法关闭与其关联的完成端口。当设计程序时,要考虑所有这些线程执行情况。


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.
大体上说,最合适的并发值是计算机的CPU数量。如果你的事务需要很长的计算时间,一个大的并发值可以允许更多的线程运行。每个完成包可能花很多时间来处理,但是多个完成包可以同时被处理。你可以用不同的分析工具测试不同的并发值来为你的应用程序挑选最合适的值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近有项目要做一个高性能网络服务器,决定下功夫搞定完成端口(IOCP),最终花了一个星期终于把它弄清楚了,并用C++写了一个版本,效率很不错。 但,从项目的总体需求来考虑,最终决定上.net平台,因此又花了一天一夜弄出了一个C#版,在这与大家分享。 一些心得体会: 1、在C#,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。 6、测试结果:(在我的笔记本上时行的,我的本本是T420 I7 8G内存) 100客户 100,000(十万次)不间断的发送接收数据(发送和接收之间没有Sleep,就一个一循环,不断的发送与接收) 耗时3004.6325 秒完成 总共 10,000,000 一千万次访问 平均每分完成 199,691.6 次发送与接收 平均每秒完成 3,328.2 次发送与接收 整个运行过程,内存消耗在开始两三分种后就保持稳定不再增涨。 看了一下对每个客户端的延迟最多不超过2秒。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值