完成端口(I/O completion)

异步过程调用(apcs)问题:
只有发overlapped请求的线程才可以提供callback函数(需要一个特定的线程为一个特定的I/O请求服务)。
完成端口(I/O completion)的优点:
不会限制handle个数,可处理成千上万个连接。I/O completion port允许一个线程将一个请求暂时保存下来,由另一个线程为它做实际服务。
并发模型与线程池:
在典型的并发模型中,服务器为每一个客户端创建一个线程,如果很多客户同时请求,则这些线程都是运行的,那么CPU就要一个个切换,CPU花费了更多的时间在线程切换,线程确没得到很多CPU时间。到底应该创建多少个线程比较合适呢,微软件帮助文档上讲应该是2*CPU个。但理想条件下最好线程不要切换,而又能象线程池一样,重复利用。I/O完成端口就是使用了线程池。
理解与使用:

第一步:
在我们使用完成端口之前,要调用CreateIoCompletionPort函数先创建完成端口对象。
定义如下:
HANDLE CreateIoCompletionPort(
 HANDLE FileHandle,
HANDLE ExistingCompletionPort,
   DWORD CompletionKey,
   DWORD NumberOfConcurrentThreads
);
FileHandle:
文件或设备的handle, 如果值为INVALID_HANDLE_VALUE则产生一个没有和任何文件handle有关系的port.( 可以用来和完成端口联系的各种句柄,文件,套接字)
ExistingCompletionPort:
NULL时生成一个新port, 否则handle会加到此port上。
CompletionKey:
用户自定义数值,被交给服务的线程。GetQueuedCompletionStatus函数时我们可以完全得到我们在此联系函数中的完成键(申请的内存块)。在GetQueuedCompletionStatus
中可以完封不动的得到这个内存块,并且使用它。
NumberOfConcurrentThreads:
参数NumberOfConcurrentThreads用来指定在一个完成端口上可以并发的线程数量。理想的情况是,一个处理器上只运行一个线程,这样可以避免线程上下文切换的开销。如果这个参数的值为0,那就是告诉系统线程数与处理器数相同。我们可以用下面的代码来创建I/O完成端口。

隐藏在之创建完成端口的秘密:
1. 创建一个完成端口
CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, dwNumberOfConcurrentThreads);
2. 设备列表,完成端口把它同一个或多个设备相关联。
CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0) ;

第二步:
根据处理器个数,创建cpu*2个工作线程:
CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,0, &ThreadID))
与此同时,服务器调用WSASocket,bind, listen, WSAAccept,之后,调用
CreateIoCompletionPort((HANDLE) Accept, CompletionPort... )把一个套接字句柄和一个完成端口绑定到一起。完成端口又同一个或多个设备相关联着,所以以套接字为基础,投递发送和请求,对I/O处理。接着,可以依赖完成端口,接收有关I/O操作完成情况的通知。再看程序里:
WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL)开始调用,这里象前面讲过的一样,既然是异步I/O,所以WSASend和WSARecv的调用会立即返回。
系统处理:
当一个设备的异步I/O请求完成之后,系统会检查该设备是否关联了一个完成端口,如果是,系统就向该完成端口的I/O完成队列中加入完成的I/O请求列。
然后我们需要从这个完成队列中,取出调用后的结果(需要通过一个Overlapped结构来接收调用的结果)。怎么知道这个队列中已经有处理后的结果呢,调用GetQueuedCompletionStatus函数。
工作线程与完成端口:
和异步过程调用不同(在一个Overlapped I/O完成之后,系统调用该回调函数。OS在有信号状态下(设备句柄),才会调用回调函数(可能有很多APCS等待处理了))
GetQueuedCompletionStatus
在工作线程内调用GetQueuedCompletionStatus函数。
GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytesTransferred,
LPDWORD lpCompletionKey,
LPOVERLAPPED *lpOverlapped,
DWORD dwMilliseconds
);
CompletionPort:指出了线程要监视哪一个完成端口。很多服务应用程序只是使用一个I/O完成端口,所有的I/O请求完成以后的通知都将发给该端口。
lpNumberOfBytesTransferred:传输的数据字节数
lpCompletionKey:

完成端口的单句柄数据指针,这个指针将可以得到我们在CreateIoCompletionPort中申请那片内存。
lpOverlapped:

重叠I/O请求结构,这个结构同样是指向我们在重叠请求时所申请的内存块,同时和lpCompletionKey,一样我们也可以利用这个内存块来存储我们要保存的任意数据。
dwMilliseconds:
等待的最长时间(毫秒),如果超时,lpOverlapped被设为NULL,函数返回False.

GetQueuedCompletionStatus功能及隐藏的秘密:
GetQueuedCompletionStatus使调用线程挂起,直到指定的端口的I/O完成队列中出现了一项或直到超时。(I/0完成队列中出现了记录)调用GetQueuedCompletionStatus时,调用线程的ID(cpu*2个线程,每个ServerWorkerThread的线程ID)就被放入该等待线程队列中。
等待线程队列很简单,只是保存了这些线程的ID。完成端口会按照后进先出的原则将一个线程队列的ID放入到释放线程列表中。
这样,I/O完成端口内核对象就知道哪些线程正在等待处理完成的I/O请求。当端口的I/O完成队列出现一项时,完成端口就唤醒(睡眠状态中变为可调度状态)等待线程队列中的一个线程。线程将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结构)和Overlapped结构地址,线程是通过GetQueuedCompletionStatus返回这些信息,等待CPU的调度。
GetQueuedCompletionStatus返回可能有多种原因,如果传递无效完成端口句柄,函数返回False,GetLastError返回一个错误(ERROR_INVALID_HANDLE),如果超时,返回False, GetLastError返回WAIT_TIMEOUT, i/o完成队列删除一项,该表项是一个成功完成的I/O请求,则返回True。
调用GetQueuedCompletionStatus的线程是后进先出的方式唤醒的,比如有4个线程等待,如果有一个I/O,最后一个调用GetQueuedCompletionStatus的线程被唤醒来处理。处理完之后,再调用GetQueuedCompletionStatus进入等待线程队列中。

深入分析完成端口线程池调度原理:
假设我们运行在2CPU的机器上。创建完成端口时指定2个并发,创建了4个工作线程加入线程池中等待完成I/O请求,且完成端口队列(先入先出)中有3个完成I/O的请求的情况:
工作线程运行, 创建了4个工作线程,调用GetQueuedCompletionStatus时,该调用线程就进入了睡眠状态,假设这个时候,I/O完成队列出现了三项,调用线程的ID就被放入该等待线程队列中。
I/O完成端口内核对象(第3个参数等级线程队列),因此知道哪些线程正在等待处理完成的I/O请求。当端口的I/O完成队列出现一项时,完成端口就唤醒(睡眠状态中变为可调度状态)等待线程队列中的一个线程(前面讲过等待线程队列是后进先出)。所以线程D将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结构)和Overlapped结构地址,线程是通过GetQueuedCompletionStatus返回这些信息。
在前面我们指定了并发线程的数目是2,所以I/O完成端口唤醒2个线程,线程D和线程C,另两个继续休眠(线程B,线程A),直到线程D处理完了,发现表项里还有要处理的,就唤醒同一线程继续处理。
线程并发量:

并发量限制了与该完成端口相关联的可运行线程的数目, 它类似阀门的作用。 当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行, 直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。所以解释了线程池中的运行线程可能会比设置的并发线程多的原因。
它的作用:
最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口达到了其并发量的极限。此时,一个正在运行中的线程调用 GetQueuedCompletionStatus时,它就会立刻从队列中取走该完成包。这样就不存在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的线程就不能运行了。
注意:如果池中的所有线程都在忙,客户请求就可能拒绝,所以要适当调整这个参数,获得最佳性能。
线程并发:D线程挂起,加入暂停线程,醒来后又加入释放线程队列。
线程的安全退出:

PostQueudCompletionStatus函数,我们可以用它发送一个自定义的包含了OVERLAPPED成员变量的结构地址,里面包含一个状态变量,当状态变量为退出标志时,线程就执行清除动作然后退出。
完成端口使用需要注意的地方:
1.在执行wsasend和wsarecv操作前,请先将overlapped结构体使用memset进行清零。

完成端口(I/O completion)相关推荐

  1. windows的IOCP(Input Output Completion Port,输入输出完成端口)

    windows的IOCP(Input Output Completion Port,输入输出完成端口) windows的IOCP(Input Output Completion Port,输入输出完成 ...

  2. .NET中的异步编程(四)- IO完成端口以及FileStream.BeginRead

    本文首发在IT168 写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型.但经过三篇文章后很多人对IO异步背后实现 ...

  3. Windows io完成端口

    Windows 提供一种称为I/O完成端口(I/O Completion Port)机制,能够让I/O的完成处理交由一个专门的线程池来完成,而线程池的线程数量是一个可配置的参数.这种做法将I/O请求的 ...

  4. HSHA多线程网络编程模型介绍

     我在上一篇的分享<Leader/Follower多线程网络模型介绍>中详细介绍了LF多线程网络模型的原理和代码分析.它的思路是减少线程上下文切换和数据拷贝时间,通过角色转换,来提高处 ...

  5. Overlapped I/O模型深入分析[转]

    简述: Overlapped I/O也称Asynchronous  I/O,异步I/O模型.异步I/O和同步I/O不同,同步I/O时,程序被挂起,一直到I/O处理完,程序才能获得控制.异步I/O,调用 ...

  6. 串口通讯编程一日通2(Overlapped IO模型)

    第一篇初步了解串口的大致运作,接下来我们看基本操作 先看串口操作的数据结构: 串口操作有几个比较重要的Struct 1.Overlapped I/O 异步I/O模型 异步I/O和同步I/O不同,同步I ...

  7. 【文件监控】之一:理解 ReadDirectoryChangesW part1

    理解 ReadDirectoryChangesW 原作者:Jim Beveridge 原文:http://qualapps.blogspot.com/2010/05/understanding-rea ...

  8. windows环境下C++实现的多种目录监控和优劣解析

    FileSystemWatcher使用原因: 最近在项目中需要在windows平台,实时的监控一个目录下各级子目录中被FTP下载到本地的文件的生成,所以研究了windows的各种监控目录变化信息的AP ...

  9. 理解 ReadDirectoryChangesW

    理解 ReadDirectoryChangesW 原作者:Jim Beveridge 原文:http://qualapps.blogspot.com/2010/05/understanding-rea ...

最新文章

  1. AngularJS 国际化——Angular-translate
  2. UE4学习-材质快捷键及材质帮助手册
  3. 使用C#調用外部程式或是執行DOS命令
  4. (多线程)leetcode1195. 交替打印字符串 最简单解法一个变量搞定
  5. 未能连接到许可证服务器 发生错误 -96,ug启动许可证错误怎么办 UG软件启动许可证错误96解决方法...
  6. linux shell 退出_dialog命令在linux系统中使用方法
  7. 大学计算机知识考试题,大学计算机基础重点知识考试试题
  8. 想从远程办公进入 toB 蓝海?它可以,你未必
  9. 数据结构与算法的分析
  10. ArrayList源码剖析
  11. VS如何定制自己的模板信息
  12. 挂载Linux镜像文件,使用镜像文件安装依赖
  13. 中国第二代支付清算体系
  14. abaqus2018+intel fortran2019+vs2015安装全记录
  15. LED字体下载,可视化大屏,数据可视化必备
  16. 微同城生活圈小程序源码系统+前后端+搭建教程
  17. linux+zip+加密+无交互,Linux下zip加密压缩
  18. OPNET win7安装
  19. 高端大气上档次Ergotron Neo-Flex+MBP Retina的组合~
  20. GTP发送端时钟网络走向

热门文章

  1. Python3,5行代码,制作Gif动图,太简单了。
  2. 软件工程导论第五章复习总结附思维导图及部分习题答案
  3. Linux系统如何更改主机名,即 [root@xxxx] 改成 [root@localhost]
  4. 吴健朱丹《爱上女主播》激吻戏被删成遗憾(图)
  5. Oracle密码过期(the password has expired)解决笔记
  6. Grafana变量介绍
  7. matlab实现聚类分析
  8. 用C++求解二元一次方程组
  9. 写给充满浮躁与抱怨的程序员
  10. Ubuntu下安装QQ——最新教程!