完成端口的原理

上传人:工**** 文档编号:565044686 上传时间:2023-04-09 格式:DOCX 页数:4 大小:14.15KB
返回 下载 相关 举报
完成端口的原理_第1页
第1页 / 共4页
完成端口的原理_第2页
第2页 / 共4页
完成端口的原理_第3页
第3页 / 共4页
完成端口的原理_第4页
第4页 / 共4页
亲,该文档总共4页,全部预览完了,如果喜欢就下载吧!
资源描述

《完成端口的原理》由会员分享,可在线阅读,更多相关《完成端口的原理(4页珍藏版)》请在金锄头文库上搜索。

1、完成端口(I/O completion):异步过程调用(apes)问题:只有发overlapped请求的线程才可以提供callback函数(需要一个特定的线程为一个 特定的 I/O 请求服务)。完成端口(I/O completio n)的优点:不会限制handle个数,可处理成千上万个连接。I/O completion port允许一个线程 将一个请求暂时保存下来,由另一个线程为它做实际服务。并发模型与线程池:在典型的并发模型中,服务器为每一个客户端创建一个线程,如果很多客户同时请求, 则这些线程都是运行的,那么CPU就要一个个切换,CPU花费了更多的时间在线程切换, 线程确没得到很多 CPU

2、 时间。到底应该创建多少个线程比较合适呢,微软件帮助文档上讲 应该是 2*CPU 个。但理想条件下最好线程不要切换,而又能象线程池一样,重复利用。 I/O 完成端口就是使用了线程池。理解与使用:第一步:在我们使用完成端口之前,要调用CreateIoCompletionPort函数先创建完成端口对 象。定义如下:HANDLE CreateIoCompletionPort(HANDLE FileHandle,HANDLE ExistingCompletionPort,DWORD CompletionKey,DWORD NumberOfConcurrentThreads);FileHandle:文件

3、或设备的 handle, 如果值为 INVALID_HANDLE_VALUE 则产生一个没有和任何文 件 handle 有关系的 port.( 可以用来和完成端口联系的各种句柄,文件,套接字) ExistingCompletionPort:NULL时生成一个新port,否则handle会加到此port上。CompletionKey:用户自定义数值,被交给服务的线程。GetQueuedCompletionStatus函数时我们可以完全得到我们在此联系函数中的完成键(申请的内存块)。在GetQueuedCompletionStatus中可以完封不动的得到这个内存块,并且使用它。NumberOfCo

4、ncurrentThreads:参数NumberOfConcurrentThreads用来指定在一个完成端口上可以并发的线程数 量。理想的情况是,一个处理器上只运行一个线程,这样可以避免线程上下文切换的开销。 如果这个参数的值为0,那就是告诉系统线程数与处理器数相同。我们可以用下面的代码来 创建 I/O 完成端口。隐藏在之创建完成端口的秘密:1 创建一个完成端口 CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, dwNumberOfConcurrentThreads);2 设备列表,完成端口把它同一个或多个设备相关联。 CreateIoCom

5、pletionPort(hDevice, hCompPort, dwCompKey, 0) ;第二步:根据处理器个数,创建cpu*2个工作线程:CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,0, &ThreadID)与此同时,服务器调用WSASocket, bi nd, liste n, WSAAccept,之后,调用 CreateIoCompletionPort(HANDLE) Accept, CompletionPort. )把一个套接字句柄 和一个完成端口绑定到一起。完成端口又同一个或多个设备相关联着,所以以套接字为基础

6、, 投递发送和请求,对I/O处理。接着,可以依赖完成端口,接收有关I/O操作完成情况的 通知。再看程序里:WSARecv(Accept, &(PerIoData-DataBuf), 1, &RecvBytes, &Flags,& (Pe rIoData-Ove rlapped), NULL)开始调用,这里象前面讲过的一样,既然是异步 I/O,所以WSASend和WSARecv的调用会立即返回。系统处理:当一个设备的异步I/O请求完成之后,系统会检查该设备是否关联了一个完成端口, 如果是,系统就向该完成端口的 I/O 完成队列中加入完成的 I/O 请求列。然后我们需要从这个完成队列中,取出调用后

7、的结果(需要通过一个Overlapped结构来接 收调用的结果)。怎么知道这个队列中已经有处理后的结果呢,调用GetQueuedCompleti on Status 函数。工作线程与完成端口:和异步过程调用不同(在一个Over lapped I/O完成之后,系统调用该回调函数。OS 在有信号状态下(设备句柄),才会调用回调函数(可能有很多APCS等待处理了)GetQueuedCompletionStatus在工作线程内调用GetQueuedCompletionStatus函数。 GetQueuedCompletionStatus(HANDLE CompletionPort,LPDWORD lp

8、NumberOfBytesTransferred,LPDWORD lpCompletionKey,LPOVERLAPPED *lpOverlapped,DWORD dwMilliseconds);Completio nPo rt:指出了线程要监视哪一个完成端口。很多服务应用程序只是使用一个I/O 完成端口,所有的I/O请求完成以后的通知都将发给该端口。lpNumbe rOfBytesTra nsfe rr ed :传输的数据字节数lpCompletionKey:完成端口的单句柄数据指针,这个指针将可以得到我们在Cr eateloCompletio nPort 中申请那片内存。lpOverlap

9、ped:重叠I/O请求结构,这个结构同样是指向我们在重叠请求时所申请的内存块,同时和 IpCompletio nKey,样我们也可以利用这个内存块来存储我们要保存的任意数据。dwMilliseconds:等待的最长时间(毫秒)如果超时,IpOverlapped被设为NULL,函数返回False.GetQueuedCompletionStatus 功能及隐藏的秘密:GetQueuedCompletio nStatus使调用线程挂起,直到指定的端口的I/O完成队列中 出现了一项或直到超时。(I/O完成队列中出现了记录)调用GetQueuedCompletionStatus 时,调用线程的 ID(c

10、pu*2 个线程,每个 ServerWorkerThread的线程ID)就被放入该等待线程队列中。等待线程队列很简单,只是保存了这些线程的ID。完成端口会按照后进先出的原则将 一个线程队列的 ID 放入到释放线程列表中。这样,I/O完成端口内核对象就知道哪些线程正在等待处理完成的I/O请求。当端口 的I/O完成队列出现一项时,完成端口就唤醒(睡眠状态中变为可调度状态)等待线程队 列中的一个线程。线程将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结 构)和Over lapped结构地址,线程是通过GetQueuedCompletio nStatus返回这些信息, 等待 CPU 的调

11、度。GetQueuedCompletio nStatus返回可能有多种原因,如果传递无效完成端口句柄, 函数返回 False,GetLastError 返回一个错误(ERROR_INVALID_HANDLE),如果超时, 返回False, GetLastError返回WAIT_TIMEOUT, i/o完成队列删除一项,该表项是一个 成功完成的 I/O 请求,则返回 True。调用GetQueuedCompletionStatus的线程是后进先出的方式唤醒的,比如有4个线 程等待,如果有一个I/O,最后一个调用GetQueuedCompletionStatus的线程被唤醒来 处理。处理完之后,再

12、调用GetQueuedCompletionStatus进入等待线程队列中。深入分析完成端口线程池调度原理:假设我们运行在2CPU的机器上。创建完成端口时指定2个并发,创建了 4个工作线程 加入线程池中等待完成I/O请求,且完成端口队列(先入先出)中有3个完成I/O的请求 的情况:工作线程运行,创建了 4个工作线程,调用GetQueuedCompletionStatus时,该调 用线程就进入了睡眠状态,假设这个时候,I/O完成队列出现了三项,调用线程的ID就被 放入该等待线程队列中。I/O完成端口内核对象(第3个参数等级线程队列),因此知道哪些线程正在等待处 理完成的I/O请求。当端口的I/O完

13、成队列出现一项时,完成端口就唤醒(睡眠状态中变 为可调度状态)等待线程队列中的一个线程(前面讲过等待线程队列是后进先出)。所以线程 D将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结构)和Overlapped 结构地址,线程是通过GetQueuedCompletio nStatus返回这些信息。在前面我们指定了并发线程的数目是2,所以I/O完成端口唤醒2个线程,线程D和 线程C,另两个继续休眠(线程B,线程A),直到线程D处理完了,发现表项里还有要 处理的,就唤醒同一线程继续处理。线程并发量:并发量限制了与该完成端口相关联的可运行线程的数目, 它类似阀门的作用。当与该完 成端口相

14、关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关 联的后续线程的执行, 直到与该完成端口相关联的可运行线程数目下降到小于该并发量为 止。所以解释了线程池中的运行线程可能会比设置的并发线程多的原因。它的作用:最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口 达到了其并发量的极限。此时,一个正在运行中的线程调 用 GetQueuedCompletionStatus 时,它就会立刻从队列中取走该完成包。这样就不存 在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的 线程就不能运行了。注意:如果池中的所有线程都在忙,客户请求就可能拒绝,所以要适当调整这个参数 获得最佳性能。线程并发:D线程挂起,加入暂停线程,醒来后又加入释放线程队列。 线程的安全退出:PostQueudCompletionStatus函数,我们可以用它发送一个自定义的包含了OVERLAPPED 成员变量的结构地址,里面包含一个状态变量,当状态变量为退出标志时, 线程就执行清除动作然后退出。完成端口使用需要注意的地方:1.在执行wsasend和wsarecv操作前,请先将overlapped结构体使用memset进行 清零。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 学术论文 > 其它学术论文

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号