﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-beautykingdom-随笔分类-windows</title><link>http://www.cppblog.com/beautykingdom/category/9502.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 09 Sep 2010 16:05:45 GMT</lastBuildDate><pubDate>Thu, 09 Sep 2010 16:05:45 GMT</pubDate><ttl>60</ttl><item><title>有关异步读写、通信 </title><link>http://www.cppblog.com/beautykingdom/archive/2010/09/06/126028.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 06 Sep 2010 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/09/06/126028.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/126028.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/09/06/126028.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/126028.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/126028.html</trackback:ping><description><![CDATA[<div><span class="Apple-tab-span" style="white-space:pre">	</span>简介</div><div><br></div><div>一般来说，简单的异步（Asynchronous） 调用是这样一种调用方式：发起者请求一个异步调用，通知执行者，然后处理其他工作，在某一个同步点等待执行者的完成；执行者执行调用的实际操作，完成后通 知发起者。可以看出，在异步调用中有两种角色：发起者和执行者，它们都是能主动运行的对象，我们称为主动对象，同时还有一个同步点，主动对象在同步点协调 同步。在本文中，我们讨论主要是通用计算机、多进程多线程的分时操作系统上的异步调用。在操作系统的角度上来看，主动对象包括了进程、线程和硬件上的IC等，至于中断，可以看作总是在某个进程或者线程的上下文借用一下CPU。而同步操作可以通过操作系统得各种同步机制：互斥锁，信号灯等等来完成。</div><div><br></div><div>我们可以先看看异步调用在Windows(本文中一般不加指出的话，都是特指NT/2000)读写文件中的应用。Windows中的ReadFile和WriteFile都提供了异步的接口。以ReadFile为例，</div><div><br></div><div>BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);</div><div><br></div><div>如果最后一个参数lpOverlapped不为NULL，并且文件以FILE_FLAG_OVERLAPPED 标志打开，那么这个调用就是异步的：ReadFile会立刻返回，如果操作没有立刻完成（返回FALSE并且GetLastError()返回 ERROR_IO_PENDING），那么调用者可以在某个时刻通过WaitForSingleObject等函数来等待中的hEvent来等待操作完成 （可能已经完成）进行同步，当操作完成以后，可以调用GetOverlappedResult者获得操作的结果，比如是否成功，读取了多少字节等等。这里 的发起者就是应用程序，而执行者就是操作系统本身，至于执行者是怎么执行的，我们会在后面的篇幅讨论。而两者的同步就是通过一个Windows Event来完成。</div><div><br></div><div>把这个异步调用的过程再抽象和扩展一些，我们可以把异步调用需要解决的问题归结为两个：一个是执行的动力，另一个是主动对象的调度。简单来说，前者是各个主动对象（线程、进程或者一些代码）是如何获得CPU，后者是各个主动对象如何协同工作， 保证操作的流程是协调正确的。一般来说，进程和线程都可以由操作系统直接调度而获得CPU，而更细粒度的，比如一些代码的调度，往往就需要一个更复杂的模 型（比如在操作系统内部的实现，这时候线程的粒度太粗了）。而主动对象的调度，当参与者较少的时候，可以通过基本的同步机制来完成，在更复杂的情况下，可 能通过一个schedule机制来做会更实际一些。</div><div><br></div><div>动力和调度</div><div><br></div><div>如前所述，异步调用主要需要解决两 个问题：执行的动力和执行的调度。最普遍的情况就是，一个主导流程的调用者进程（线程），一个或多个工作者进程（线程），通过操作系统提供的同步机制来完 成异步调用。这个同步机制在扩展化的情形下，是一个或多个栅栏Barrier，对应于每个同步的执行点。所有需要在这个执行点同步的主动对象会等待相应的 Barrier，直到所有对象都完成。在一些简化的情形，比如说工作者并不关心调用者的同步，那么这个Barrier可以简化成信号灯，在只有一个工作者 的情况下，可以简化成一个Windows事件Event或者条件变量 Condition Variable。</div><div><br></div><div>现在来考虑复杂的情 形。假设我们用一些线程来协作完成一项工作，各个线程的执行之间有先后顺序上的限制，而操作系统就是这项工作的调度者，负责在适当的时候调度适当的线程来 获得CPU。显然，并发执行中的一个线程对于另外一个线程来说，本质上就是异步的，假如它们之间有调用关系，那也就是一个异步调用。而操作系统可以通过基 本的同步机制使得合适的线程才被调度，其他未完成的线程则处于等待状态。举例说，我们有4个线程A,B,C,D来完成一项工作，其中的顺序限制是 A&gt;B;C&gt;D，&#8220;&gt;&#8221;表示左边的线程完成必须先于右边的线程执行，而&#8220;;&#8221;表示两个线程可以同时进行。同时假设B的一个操作需要调用 C来完成，显而易见，这时候这个操作就是一个异步调用。我们可以在每个&#8220;&gt;&#8221;的位置设定一个同步点，然后通过一个信号灯来完成同步。线程B，C等待 第一个信号灯，而D会等待第二个信号灯。这个例子的动力和调度都是通过操作系统的基本机制（线程调度和同步机制）来完成。</div><div><br></div><div>把这个过程抽象一下，可以描述为：若干个主动对象（包括代码）协调来完成一项工作，通过一个调度器来调度，实际上，这个调度器可能只是一些调度规则。显 然，进程或者线程只要被调度就能获得CPU，所以我们主要考虑代码（比如一个函数）怎么样才能获得执行。用工作者线程来调用这个函数显然是直观和通用的一 个方案。事实上，在用户空间(user space)或者用户态(user mode)，这个方法是很常用的。而在内核态(kernel mode)，则可以通过中断来获得CPU，这个通过注册IDT入 口和触发软中断就可以完成。硬件设备上的IC是另一个动力之源。而主动对象的调度，最基本的也是前面说的各种同步机制。另一个常用的机制就是回调函数，需 要注意的是，回调函数一般会发生在跟调用者不一样的上下文，比如说同一个进程的不同线程，这个差别会带来一些限制。如果需要回调发生在调用者的进程（线 程）上下文，则需要一些类似Unix下的signal或者Windows下的APC机制，这一点我们在后面会有所阐述。那么在回调函数里面一般作些什么事情呢？最常用的， 跟同步机制结合在一起，当然就是释放一个互斥锁，信号灯或者Windows Event（Unix的条件变量）等等，从而使得等待同步的其他对象可以得到调度而重新执行，实际上，也可以看作是通知调度器（操作系统）某些主动对象 （等待同步的）可以重新被调度了，从而调度器重新调度。但是对于另外一些调度器，在这个过程中可能不需要同步对象的参与。在一些极端一些的例子里，调度甚 至不要求严格有序的。</div><div><br></div><div>在实际应用中，根据环境的限制，异步调用的动力和调度的实现方式可以有很大差别。我们会在后面的例子里加以说明。 操作系统中的异步：Windows的异步I/O。</div><div><br></div><div>Windows NT/2000是一个抢占式的分时操作系统。Windows的调度单位是线程，它的 I/O架构是完全异步的，也就是说同步的I/O实际上都基于异步I/O来完成。一个用户态的线程请求一个I/O的时候会导致一个运行状态从user mode到kernel mode的转变（操作系统把内核映射到每个进程的2G-4G的地址上，对于每个进程都是一样的）。这个过程是通过中断调用内核输出的一些System Service来完成，比如说ReadFile实际上会执行NtReadFile（ZwReadFile），需要注意的是，运行上下文仍然是当前线程。 NtReadFile的实现则基于Windows内核的异步I/O框架，在I/O Manager的协助下完成。需要指出的是，I/O Manager只是由若干API构成的一个抽象概念，并没有一个真正的I/O Manager线程在运行。</div><div><br></div><div>Windows的I/O驱动程序是层次堆积的。每个驱动程序会提供一致的接口以供初始化、清理和功能调用。驱动程序的调用基于I/O请求包（I/O Request Packet, IRP），而不是像普通的函数调用那样使用栈来传递参数。操作系统和PnP管理器根据注册表在 适当的时机初始化和清理相应的驱动程序。在一般的功能调用的时候，IRP里面会指定功能调用号码以及相应的上下文或者参数（I/O stack location）。一个驱动程序可能调用别的驱动程序，这个过程可能是同步的（线程上下文不改变)，也可能是异步的。NtReadFile的实现，大致 是向最上层的驱动程序发出一个或多个IRP，然后等待相应事件的完成（同步的情况），或者直接返回（带Overlapped的情况），这些都在发起请求的 线程执行。</div><div><br></div><div>当驱动程序处理IRP的时候，它可能立刻完成，也可能在中断里才能完成，比如说，往硬件设备发出一个请求（通常可以是写 I/O port），当设备完成操作的时候会触发一个中断，然后在中断处理函数里得到操作结果。Windows有两类中断，硬件设备的中断和软中断，分成若干个不 同的优先级（IRQL）。软中断主要有两种：DPC(Delayed Procedure Call)和APC(Asynchronous Procedure Call)，都处于较低的优先级。驱动程序可以为硬件中断注册ISR(Interrupt Service Routine)，一般就是修改IDT某个条目的入口。同样，操作系统也会为DPC和APC注册适当的中断处理例程（也是在IDT中）。</div><div><br></div><div>值得指出的是，DPC是跟处理器相关的，每个处理器会有一个DPC队列，而APC是跟线程相关的，每个线程会有它的APC队列（实际上包括一个 Kernel APC队列和User APC队列，它们的调度策略有所区别），可以想象，APC并不算严格意义上的中断，因为中断可能发生在任何一个线程的上下文中，它被称为中断，主要是因为 IRQL的提升（从PASSIVE到APC），APC的调度一般在线程切换等等情形下进行。当中断发生的时候，操作系统会调用中断处理例程，对于硬件设备 的ISR，一般处理是关设备中断，发出一个DPC请求，然后返回。不在设备的中断处理中使用太多的CPU时间，主要考虑是否则可能丢失别 的中断。由于硬件设备中断的IRQL比DPC中断的高，所以在ISR里面DPC会阻塞，直到ISR返回IRQL回到较低的水平，才会触发DPC中断，在 DPC中断里执行从硬件设备读取数据以及重新请求、开中断等操作。ISR或者DPC可能在任何被中断的线程上下文（arbitrary thread context）执行，事实上线程的上下文是不可见的，可以认为是系统借用一下时间片而已。</div><div><br></div><div>总的来说，Windows的异步I/O架 构中，主要有两种动力，一是发起请求的线程，一部分内核代码会在这个线程上下文执行，二是ISR和DPC，这部分内核代码会在中断里完成，可能使用任何一 个线程的上下文。而调度常见使用回调和事件（KEVENT），比如说在往下一层的驱动程序发出请求的时候，可以指定一个完成例程Completion Routine，当下层的驱动完成这个请求的时候会调用这个例程，而往往在这个例程里，就是简单的触发一下一个事件。 另外可以顺便提一下Linux。Linux 2.6也有类似的中断机制，它有更多的软中断优先级，即不同优先级的softirq，而类似于DPC，Linux也提供了专门的软中断，对应DPC的就是 tasklet。Linux没有一个像windows这么一致的层次驱动程序架构，所以它的异步I/O稍微粗糙一些，主要是通过以前的一些阻塞点，现在直 接返回-EIOCBRETRY，而让调用者在合适的时机继续重试。在这个方法中，可以认为整个操作由一个函数完成，每次操作有进展时，都把这个函数从头执 行一遍，当然已经完成的部分就不会再有实际的I/O。这样的最大好处是原有的文件系统和驱动程序不用完全重写。而对于同步调用，只要阻塞就可以了，这样对 系统的修改较小。这时候，要提供POSIX aio的语义，就可能需要提供一些用户线程来完成重试的过程了（回想Windows可以通过中断和DPC完成的）。而对于Solaris，也是类似的处理，如果设备支持异步I/O，那就通过中断可以完成，否则就使用内部的LWP来模拟。</div><div>应用程序：一个异步的HTTP服务器的设计</div><div><br></div><div>假设我们要设计一个HTTP服务器，它的设计目标包括：高并发性、精简 （部分支持HTTP/1.1）、支持plug-in结构。在不少场合可能都有这个需求。总体上来说，HTTP服务器可以类比成一个基于多线程的操作系 统：OS调度每个工作线程在适当的时候获得执行，而工作线程提供服务（也就是处理HTTP请求）。在这个基础上，主要的考虑就是调度粒度的大小，粒度太大 的时候并发性会降低，而粒度太小又可能因为任务切换（考虑OS的Context Switching）而导致效率降低，所以这又是一个折衷的结果。类似于Apache（以及其他的HTTP服务器），我们可以把一个HTTP处理过程分为 若干个状态，基于这些状态可以构造出一个HTTP处理的状态机。这种情况下，我们就可以把每个状态的处理作为调度的粒度。一个调度过程就是：一个工作线程 从全局的任务队列里取出一个HTTP_Context结构；根据当前的状态完成相应处理；然后根据状态机设置下一个状态；再放回到全局的任务队列里。这样 子，若干个HTTP状态就可以通过这个调度策略构成一个完整HTTP处理过程。显而易见，一个状态对于下一个状态处理的调用都可以认为是异步的。一个 HTTP状态机的设计如下图所示。</div><div><img src="http://www.cppblog.com/images/cppblog_com/beautykingdom/gr4o3x9474bo.jpg" id="" width="512" height="398" vspace="0" hspace="0" border="" align="baseline" alt="" longdesc=""><br></div><div>　　　　</div><div>图1. HTTP状态机</div><div><br></div><div>工作线程的函数其实就是两个操作：从状态队列里取出一个HTTP_Context，调用HTTP_Context的service()函数，周而复此。 在这个架构上，就很容易引入异步I/O和Plug-in的机制了。事实上我们也可以使用基于事件（例如select/poll）的I/O策略来模拟异步I /O，实现中使用一个用户线程就可以了。</div><div><br></div><div>对于异步I/O和Plug-in的调用，我们也是采用类似于Linux 2.6里面aio的重试方案，而异步完成的时候采用回调函数。在某个状态上，如果系统需要I/O操作（recv或者send），则会请求一个异步I /O（操作系统提供的异步I/O或者由用户线程模拟的异步I/O），这时候相应的HTTP_Context不会重新回到状态队列里，而在I/O完成的回调 函数里面才会重新放回到状态队列，得到重新调度的机会。HTTP_Context得到重新调度的时候会检查I/O状态（这个可以通过一些标志位来完成）， 如果已经完成，则处理然后设置下一状态，重新调度，否则可以重新请求一个新的I/O请求。Plug-in也可以使用类似的方案，比如说一个Plug-in 要跟外部的一个服务器通信，这时候就可以在通信完成的时候才把HTTP_Context重新放回到状态队列。显然，Plug-in跟HTTP状态是多对多 的关系，一个Plug-in可以在若干个关心的状态注册自身，同时还可以设置一些short-path来提高处理的效率。</div><div><br></div><div>结论</div><div><br></div><div>总的来说，异步调用的设计和应用归根结底就是对多个主动对象的管理问题：如何提供执行的动力以及如何保证执行的顺序逻辑。主要考虑的问题是主动对象的粒 度以及执行方式，同步或者回调来完成顺序的调度，或者使用近似的调度而加一些鲁棒的错误处理机制来保证语义的正确。后者可以考虑在使用基于事件的 socket的时候，readable事件的通知可以是冗余的，或者说可以比实际中发生的readable事件更多，这个时候使用非阻塞的socket, 有些read()（或者recv()）会直接返回EWOULDBLOCK，系统只要考虑处理这种情况（使用non blocking socket而不是blocking socket），当例外的情况不多的时候是可以接受的。这时候可以说事件的报告就只是近似的。</div><div>from:</div><div><a href="http://hi.baidu.com/hytjfxk/blog/item/d9262cdfcb298c14632798b3.html">http://hi.baidu.com/hytjfxk/blog/item/d9262cdfcb298c14632798b3.html</a></div><div><br></div><img src ="http://www.cppblog.com/beautykingdom/aggbug/126028.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-09-06 17:33 <a href="http://www.cppblog.com/beautykingdom/archive/2010/09/06/126028.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个基于完成端口的TCP Server Framework,浅析IOCP</title><link>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 25 Aug 2010 12:42:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/124731.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/124731.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/124731.html</trackback:ping><description><![CDATA[<span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">如果你不投递（POST）Overlapped&nbsp;I/O，那么I/O Completion&nbsp;Ports&nbsp;只能为你提供一个Queue.&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp; CreateIoCompletionPort的NumberOfConcurrentThreads：</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">1.只有当第二个参数ExistingCompletionPort为NULL时它才有效，它是个max threads limits.</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">2.大家有谁把它设置为超出cpu个数的值，当然不只是cpu个数的2倍，而是下面的MAX_THREADS 100甚至更大。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">对于这个值的设定，msdn并没有说非得设成cpu个数的2倍，而且也没有把减少线程之间上下文交换这些影响扯到这里来。I/O Completion Ports MSDN:"If your transaction required a</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>lengthy computation</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">, a</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>larger</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>concurrency value</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">will allow more threads to run. Each completion packet may take longer to finish,</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>but more completion packets will be processed at the same time</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">. "。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp; 对于struct OVERLAPPED，我们常会如下扩展，</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct {</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WSAOVERLAPPED</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>overlapped</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">; //</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><span style="line-height: 18px; color: rgb(255, 0, 0); ">must be first member</span>?</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;是的，必须是第一个。如果你不肯定，你可以试试。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; SOCKET client_s;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; SOCKADDR_IN client_addr;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WORD</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>optCode</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">;//</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>1--read,2--send.</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; 有人常会定义这个数据成员，但也有人不用，争议在send/WSASend,此时的同步和异步是否有必要？&nbsp;至少我下面的server更本就没用它。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; char buf[MAX_BUF_SIZE];</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WSABUF</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>wsaBuf</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">;//</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><span style="line-height: 18px; color: rgb(255, 0, 0); "><strong>inited ?</strong></span></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; 这个不要忘了！</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; DWORD numberOfBytesTransferred;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; DWORD flags;&nbsp;&nbsp;&nbsp;</span><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">}QSSOverlapped;//<strong>for per connection<br></strong>我下面的server框架的基本思想是:<br>One connection&nbsp;VS one thread in worker thread pool&nbsp;,worker thread performs completionWorkerRoutine.<br>A&nbsp;Acceptor thread 专门用来accept socket,关联至IOCP,并WSARecv:post Recv Completion Packet to IOCP.<br>在completionWorkerRoutine中有以下的职责:<br>1.handle request,当忙时增加completionWorkerThread数量但不超过maxThreads,post Recv Completion Packet to IOCP.<br>2.timeout时检查是否空闲和当前completionWorkerThread数量,当空闲时保持或减少至minThreads数量.<br>3.对所有Accepted-socket管理生命周期,这里利用系统的keepalive probes,若想实现业务层"心跳探测"只需将QSS_SIO_KEEPALIVE_VALS_TIMEOUT 改回系统默认的2小时.<br><strong>下面结合源代码,浅析一下IOCP</strong>:<br><strong>socketserver.h<br></strong>#ifndef __Q_SOCKET_SERVER__<br>#define __Q_SOCKET_SERVER__<br>#include &lt;winsock2.h&gt;<br>#include &lt;mstcpip.h&gt;<br>#define QSS_SIO_KEEPALIVE_VALS_TIMEOUT 30*60*1000<br>#define QSS_SIO_KEEPALIVE_VALS_INTERVAL 5*1000</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#define MAX_THREADS 100<br>#define MAX_THREADS_MIN&nbsp; 10<br>#define MIN_WORKER_WAIT_TIMEOUT&nbsp; 20*1000<br>#define MAX_WORKER_WAIT_TIMEOUT&nbsp; 60*MIN_WORKER_WAIT_TIMEOUT</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#define MAX_BUF_SIZE 1024<br><br>/*当Accepted socket和socket关闭或发生异常时回调CSocketLifecycleCallback*/<br>typedef void (*CSocketLifecycleCallback)(SOCKET cs,int lifecycle);//lifecycle:0:OnAccepted,-1:OnClose//注意OnClose此时的socket未必可用,可能已经被非正常关闭或其他异常.<br><br>/*协议处理回调*/<br>typedef int (*InternalProtocolHandler)(LPWSAOVERLAPPED overlapped);//return -1:SOCKET_ERROR</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct Q_SOCKET_SERVER SocketServer;<br>DWORD initializeSocketServer(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout);<br>DWORD startSocketServer(SocketServer *ss);<br>DWORD shutdownSocketServer(SocketServer *ss);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#endif<br>&nbsp;<strong>qsocketserver.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简称 qss,相应的OVERLAPPED简称qssOl.<br></strong>#include "socketserver.h"<br>#include "stdio.h"<br>typedef struct {&nbsp;&nbsp;<br>&nbsp; WORD&nbsp;<strong>passive</strong>;//<strong>daemon</strong><br>&nbsp; WORD port;<br>&nbsp; WORD minThreads;<br>&nbsp; WORD maxThreads;<br>&nbsp; volatile long&nbsp;<strong>lifecycleStatus</strong>;//0-created,1-starting, 2-running,3-stopping,4-exitKeyPosted,5-stopped&nbsp;<br>&nbsp; long&nbsp; workerWaitTimeout;//wait timeout&nbsp;&nbsp;<br>&nbsp; CRITICAL_SECTION QSS_LOCK;<br>&nbsp; volatile long&nbsp;<strong>workerCounter</strong>;<br>&nbsp; volatile long&nbsp;<strong>currentBusyWorkers</strong>;<br>&nbsp; volatile long&nbsp;<strong>CSocketsCounter</strong>;//<strong>Accepted-socket引用计数<br></strong>&nbsp; CSocketLifecycleCallback cslifecb;<br>&nbsp; InternalProtocolHandler protoHandler;<br>&nbsp; WORD wsaVersion;//=MAKEWORD(2,0);<br>&nbsp; WSADATA wsData;<br>&nbsp; SOCKET server_s;<br>&nbsp; SOCKADDR_IN serv_addr;<br>&nbsp; HANDLE iocpHandle;<br>}QSocketServer;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct {<br>&nbsp; WSAOVERLAPPED overlapped;&nbsp;&nbsp;<br>&nbsp; SOCKET client_s;<br>&nbsp; SOCKADDR_IN client_addr;<br>&nbsp; WORD optCode;<br>&nbsp; char buf[MAX_BUF_SIZE];<br>&nbsp; WSABUF wsaBuf;<br>&nbsp; DWORD numberOfBytesTransferred;<br>&nbsp; DWORD flags;<br>}QSSOverlapped;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>acceptorRoutine</strong>(LPVOID);<br>DWORD&nbsp;&nbsp;<strong>completionWorkerRoutine</strong>(LPVOID);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static void adjustQSSWorkerLimits(QSocketServer *qss){<br>&nbsp;&nbsp;/*adjust size and timeout.*/<br>&nbsp;&nbsp;/*if(qss-&gt;maxThreads &lt;= 0) {<br>&nbsp;&nbsp;&nbsp;qss-&gt;maxThreads = MAX_THREADS;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (qss-&gt;maxThreads &lt; MAX_THREADS_MIN) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;maxThreads = MAX_THREADS_MIN;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;minThreads &gt;&nbsp; qss-&gt;maxThreads) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads =&nbsp; qss-&gt;maxThreads;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;minThreads &lt;= 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(1 == qss-&gt;maxThreads) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads = qss-&gt;maxThreads/2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;workerWaitTimeout&lt;MIN_WORKER_WAIT_TIMEOUT)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;workerWaitTimeout=MIN_WORKER_WAIT_TIMEOUT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;workerWaitTimeout&gt;MAX_WORKER_WAIT_TIMEOUT)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;workerWaitTimeout=MAX_WORKER_WAIT_TIMEOUT;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct{<br>&nbsp;QSocketServer * qss;<br>&nbsp;HANDLE th;<br>}QSSWORKER_PARAM;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static WORD addQSSWorker(QSocketServer *qss,WORD addCounter){<br>&nbsp;WORD res=0;<br>&nbsp;if(qss-&gt;workerCounter&lt;qss-&gt;minThreads||(qss-&gt;currentBusyWorkers==qss-&gt;workerCounter&amp;&amp;qss-&gt;workerCounter&lt;qss-&gt;maxThreads)){<br>&nbsp;&nbsp;DWORD threadId;<br>&nbsp;&nbsp;QSSWORKER_PARAM * pParam=NULL;<br>&nbsp;&nbsp;int i=0;&nbsp;&nbsp;<br>&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(qss-&gt;workerCounter+addCounter&lt;=qss-&gt;maxThreads)<br>&nbsp;&nbsp;&nbsp;for(;i&lt;addCounter;i++)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pParam=malloc(sizeof(QSSWORKER_PARAM));<br>&nbsp;&nbsp;&nbsp;&nbsp;if(pParam){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParam-&gt;th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)completionWorkerRoutine,pParam,CREATE_SUSPENDED,&amp;threadId);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParam-&gt;qss=qss;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResumeThread(pParam-&gt;th);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;workerCounter++,res++;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;}&nbsp;&nbsp;<br>&nbsp;return res;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static void SOlogger(const char * msg,SOCKET s,int clearup){<br>&nbsp;perror(msg);<br>&nbsp;if(s&gt;0)<br>&nbsp;closesocket(s);<br>&nbsp;if(clearup)<br>&nbsp;WSACleanup();<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static int _InternalEchoProtocolHandler(LPWSAOVERLAPPED overlapped){<br>&nbsp;QSSOverlapped *qssOl=(QSSOverlapped *)overlapped;<br>&nbsp;<br>&nbsp;printf("numOfT:%d,WSARecvd:%s,\n",qssOl-&gt;numberOfBytesTransferred,qssOl-&gt;buf);<br>&nbsp;//Sleep(500);&nbsp;<br>&nbsp;return send(qssOl-&gt;client_s,qssOl-&gt;buf,qssOl-&gt;numberOfBytesTransferred,0);<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>initializeSocketServer</strong>(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout){<br>&nbsp;QSocketServer * qss=malloc(sizeof(QSocketServer));<br>&nbsp;qss-&gt;passive=passive&gt;0?1:0;<br>&nbsp;qss-&gt;port=port;<br>&nbsp;qss-&gt;minThreads=minThreads;<br>&nbsp;qss-&gt;maxThreads=maxThreads;<br>&nbsp;qss-&gt;workerWaitTimeout=workerWaitTimeout;<br>&nbsp;qss-&gt;wsaVersion=MAKEWORD(2,0);&nbsp;<br>&nbsp;qss-&gt;lifecycleStatus=0;<br>&nbsp;InitializeCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;qss-&gt;workerCounter=0;<br>&nbsp;qss-&gt;currentBusyWorkers=0;<br>&nbsp;qss-&gt;CSocketsCounter=0;<br>&nbsp;qss-&gt;cslifecb=cslifecb,qss-&gt;protoHandler=protoHandler;<br>&nbsp;if(!qss-&gt;protoHandler)<br>&nbsp;&nbsp;qss-&gt;protoHandler=_InternalEchoProtocolHandler;&nbsp;<br>&nbsp;adjustQSSWorkerLimits(qss);<br>&nbsp;*ssp=(SocketServer *)qss;<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>startSocketServer</strong>(SocketServer *ss){&nbsp;<br>&nbsp;QSocketServer * qss=(QSocketServer *)ss;<br>&nbsp;if(qss==NULL||InterlockedCompareExchange(&amp;qss-&gt;lifecycleStatus,1,0))<br>&nbsp;&nbsp;return 0;&nbsp;<br>&nbsp;qss-&gt;serv_addr.sin_family=AF_INET;<br>&nbsp;qss-&gt;serv_addr.sin_port=htons(qss-&gt;port);<br>&nbsp;qss-&gt;serv_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("127.0.0.1");<br>&nbsp;if(WSAStartup(qss-&gt;wsaVersion,&amp;qss-&gt;wsData)){&nbsp;&nbsp;<br>&nbsp; /*<strong>这里还有个插曲就是这个WSAStartup被调用的时候,它居然会启动一条额外的线程,当然稍后这条线程会自动退出的</strong>.不知<strong>WSAClearup</strong>又会如何?......*/</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;SOlogger("WSAStartup failed.\n",0,0);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;qss-&gt;server_s=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);<br>&nbsp;if(qss-&gt;server_s==INVALID_SOCKET){&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("socket failed.\n",0,1);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;if(bind(qss-&gt;server_s,(LPSOCKADDR)&amp;qss-&gt;serv_addr,sizeof(SOCKADDR_IN))==SOCKET_ERROR){&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("bind failed.\n",qss-&gt;server_s,1);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;if(<strong>listen</strong>(qss-&gt;server_s,<strong>SOMAXCONN</strong>)==SOCKET_ERROR)/*这里来谈谈<strong>backlog</strong>,很多人不知道设成何值,我见到过1,5,50,100的,有人说设定的越大越耗资源,的确,这里设成SOMAXCONN不代表windows会真的使用SOMAXCONN,而是" If set to SOMAXCONN, the underlying service provider responsible for socket&nbsp;<em>s</em>&nbsp;will set the backlog to a maximum&nbsp;<strong>reasonable</strong>&nbsp;value. "，同时在现实环境中，不同操作系统支持TCP缓冲队列有所不同，所以还不如让操作系统来决定它的值。像Apache这种服务器：<br>#ifndef DEFAULT_LISTENBACKLOG<br>#define DEFAULT_LISTENBACKLOG 511<br>#endif<br>*/<br>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("listen failed.\n",qss-&gt;server_s,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;qss-&gt;iocpHandle=<strong>CreateIoCompletionPort</strong>(<strong>INVALID_HANDLE_VALUE</strong>,NULL,0,<strong>/*NumberOfConcurrentThreads--&gt;*/qss-&gt;maxThreads</strong>);<br>&nbsp;//initialize worker for completion routine.<br>&nbsp;addQSSWorker(qss,qss-&gt;minThreads);&nbsp;&nbsp;<br>&nbsp;qss-&gt;lifecycleStatus=2;<br>&nbsp;{<br>&nbsp;&nbsp;QSSWORKER_PARAM * pParam=malloc(sizeof(QSSWORKER_PARAM));<br>&nbsp;&nbsp;pParam-&gt;qss=qss;<br>&nbsp;&nbsp;pParam-&gt;th=NULL;<br>&nbsp;&nbsp;if(qss-&gt;<strong>passive</strong>){<br>&nbsp;&nbsp;&nbsp;DWORD threadId;<br>&nbsp;&nbsp;&nbsp;pParam-&gt;th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)acceptorRoutine,pParam,0,&amp;threadId);&nbsp;<br>&nbsp;&nbsp;}else<br>&nbsp;&nbsp;&nbsp;return acceptorRoutine(pParam);<br>&nbsp;}<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>shutdownSocketServer</strong>(SocketServer *ss){<br>&nbsp;QSocketServer * qss=(QSocketServer *)ss;<br>&nbsp;if(qss==NULL||InterlockedCompareExchange(&amp;qss-&gt;lifecycleStatus,3,2)!=2)<br>&nbsp;&nbsp;return 0;&nbsp;<br>&nbsp;closesocket(qss-&gt;server_s/*<strong>listen-socket</strong>*/);//<strong>..other accepted-sockets associated with the listen-socket will not be closed,except WSACleanup is called..</strong>&nbsp;<br>&nbsp;if(qss-&gt;CSocketsCounter==0)<br>&nbsp;&nbsp;qss-&gt;lifecycleStatus=4,PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);<br>&nbsp;WSACleanup();&nbsp;&nbsp;<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>acceptorRoutine</strong>(LPVOID ss){<br>&nbsp;QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;<br>&nbsp;QSocketServer * qss=pParam-&gt;qss;<br>&nbsp;HANDLE curThread=pParam-&gt;th;<br>&nbsp;QSSOverlapped *qssOl=NULL;<br>&nbsp;SOCKADDR_IN client_addr;<br>&nbsp;int client_addr_leng=sizeof(SOCKADDR_IN);<br>&nbsp;SOCKET cs;&nbsp;<br>&nbsp;free(pParam);<br>&nbsp;while(1){&nbsp;&nbsp;<br>&nbsp;&nbsp;printf("accept starting.....\n");<br>&nbsp;&nbsp;<strong>cs/*Accepted-socket*/</strong>=<strong>accept</strong>(qss-&gt;server_s,(LPSOCKADDR)&amp;client_addr,&amp;client_addr_leng);<br>&nbsp;&nbsp;if(cs==INVALID_SOCKET)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;printf("accept failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{//<strong>SO_KEEPALIVE,SIO_KEEPALIVE_VALS</strong>&nbsp;这里是利用系统的"<strong>心跳探测</strong>",keepalive probes.linux:setsockopt,SOL_TCP:TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct tcp_keepalive alive,aliveOut;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int so_keepalive_opt=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD outDW;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!setsockopt(cs,SOL_SOCKET,SO_KEEPALIVE,(char *)&amp;so_keepalive_opt,sizeof(so_keepalive_opt))){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.onoff=TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.keepalivetime=QSS_SIO_KEEPALIVE_VALS_TIMEOUT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.keepaliveinterval=QSS_SIO_KEEPALIVE_VALS_INTERVAL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(WSAIoctl(cs,SIO_KEEPALIVE_VALS,&amp;alive,sizeof(alive),&amp;aliveOut,sizeof(aliveOut),&amp;outDW,NULL,NULL)==SOCKET_ERROR){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("WSAIoctl SIO_KEEPALIVE_VALS failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("setsockopt SO_KEEPALIVE failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;CreateIoCompletionPort((HANDLE)cs,qss-&gt;iocpHandle,cs,0);<br>&nbsp;&nbsp;if(qssOl==NULL){<br>&nbsp;&nbsp;&nbsp;qssOl=malloc(sizeof(QSSOverlapped));&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;qssOl-&gt;client_s=cs;<br>&nbsp;&nbsp;qssOl-&gt;wsaBuf.len=MAX_BUF_SIZE,qssOl-&gt;wsaBuf.buf=qssOl-&gt;buf,qssOl-&gt;numberOfBytesTransferred=0,qssOl-&gt;flags=0;//initialize WSABuf.<br>&nbsp;&nbsp;memset(&amp;qssOl-&gt;overlapped,0,sizeof(WSAOVERLAPPED));&nbsp;&nbsp;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;DWORD lastErr=GetLastError();<br>&nbsp;&nbsp;&nbsp;int ret=0;<br>&nbsp;&nbsp;&nbsp;SetLastError(0);<br>&nbsp;&nbsp;&nbsp;ret=WSARecv(cs,&amp;qssOl-&gt;wsaBuf,1,&amp;qssOl-&gt;numberOfBytesTransferred,&amp;qssOl-&gt;flags,&amp;qssOl-&gt;overlapped,NULL);<br>&nbsp;&nbsp;&nbsp;if(ret==0||(ret==SOCKET_ERROR&amp;&amp;GetLastError()==WSA_IO_PENDING)){<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedIncrement(&amp;qss-&gt;<strong>CSocketsCounter</strong>);//<strong>Accepted-socket计数递增.</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;cslifecb)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;cslifecb(cs,0);<br>&nbsp;&nbsp;&nbsp;&nbsp;qssOl=NULL;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(!GetLastError())<br>&nbsp;&nbsp;&nbsp;&nbsp;SetLastError(lastErr);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;printf("accept flags:%d ,cs:%d.\n",GetLastError(),cs);<br>&nbsp;}//end while.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;if(qssOl)<br>&nbsp;&nbsp;free(qssOl);<br>&nbsp;if(qss)<br>&nbsp;&nbsp;shutdownSocketServer((SocketServer *)qss);<br>&nbsp;if(curThread)<br>&nbsp;&nbsp;CloseHandle(curThread);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static int postRecvCompletionPacket(QSSOverlapped * qssOl,int SOErrOccurredCode){&nbsp;<br>&nbsp;int SOErrOccurred=0;&nbsp;<br>&nbsp;DWORD lastErr=GetLastError();<br>&nbsp;SetLastError(0);<br>&nbsp;//SOCKET_ERROR:-1,WSA_IO_PENDING:997<br>&nbsp;if(WSARecv(qssOl-&gt;client_s,&amp;qssOl-&gt;wsaBuf,1,&amp;qssOl-&gt;numberOfBytesTransferred,&amp;qssOl-&gt;flags,&amp;qssOl-&gt;overlapped,NULL)==SOCKET_ERROR<br>&nbsp;&nbsp;&amp;&amp;GetLastError()!=WSA_IO_PENDING)//this case lastError maybe 64, 10054&nbsp;<br>&nbsp;{<br>&nbsp;&nbsp;SOErrOccurred=SOErrOccurredCode;&nbsp;&nbsp;<br>&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;if(!GetLastError())<br>&nbsp;&nbsp;SetLastError(lastErr);&nbsp;<br>&nbsp;if(SOErrOccurred)<br>&nbsp;&nbsp;printf("worker[%d] postRecvCompletionPacket SOErrOccurred=%d,preErr:%d,postedErr:%d\n",GetCurrentThreadId(),SOErrOccurred,lastErr,GetLastError());<br>&nbsp;return SOErrOccurred;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>completionWorkerRoutine</strong>(LPVOID ss){<br>&nbsp;QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;<br>&nbsp;QSocketServer * qss=pParam-&gt;qss;<br>&nbsp;HANDLE curThread=pParam-&gt;th;<br>&nbsp;QSSOverlapped * qssOl=NULL;<br>&nbsp;DWORD numberOfBytesTransferred=0;<br>&nbsp;ULONG_PTR completionKey=0;<br>&nbsp;int postRes=0,handleCode=0,exitCode=0,SOErrOccurred=0;&nbsp;<br>&nbsp;free(pParam);<br>&nbsp;while(!exitCode){<br>&nbsp;&nbsp;SetLastError(0);<br>&nbsp;&nbsp;if(GetQueuedCompletionStatus(qss-&gt;iocpHandle,&amp;numberOfBytesTransferred,&amp;completionKey,(LPOVERLAPPED *)&amp;qssOl,qss-&gt;workerWaitTimeout)){<br>&nbsp;&nbsp;&nbsp;if(<strong>completionKey==-1</strong>&amp;&amp;qss-&gt;lifecycleStatus&gt;=4)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] completionKey -1:%d \n",GetCurrentThreadId(),GetLastError());<br>&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;workerCounter&gt;1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;exitCode=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;<strong>break;<br></strong>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;if(numberOfBytesTransferred&gt;0){&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedIncrement(&amp;qss-&gt;currentBusyWorkers);<br>&nbsp;&nbsp;&nbsp;&nbsp;addQSSWorker(qss,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;handleCode=qss-&gt;protoHandler((LPWSAOVERLAPPED)qssOl);&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedDecrement(&amp;qss-&gt;currentBusyWorkers);&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;if(handleCode&gt;=0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=postRecvCompletionPacket(qssOl,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=2;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}else{<br>&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] numberOfBytesTransferred==0 ***** closesocket servS or cs *****,%d,%d ,ol is:%d\n",GetCurrentThreadId(),GetLastError(),completionKey,qssOl==NULL?0:1);<br>&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;}else{ //GetQueuedCompletionStatus rtn FALSE, lastError 64 ,<strong>995</strong>[<strong>timeout worker thread exit</strong>.] ,WAIT_TIMEOUT:258&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(qssOl){<br>&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=postRecvCompletionPacket(qssOl,4);<br>&nbsp;&nbsp;&nbsp;}else {&nbsp;&nbsp;&nbsp;&nbsp;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] GetQueuedCompletionStatus F:%d \n",GetCurrentThreadId(),GetLastError());<br>&nbsp;&nbsp;&nbsp;&nbsp;if(GetLastError()!=WAIT_TIMEOUT){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exitCode=2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}else{//wait timeout&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;lifecycleStatus!=4&amp;&amp;qss-&gt;currentBusyWorkers==0&amp;&amp;qss-&gt;workerCounter&gt;qss-&gt;minThreads){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;lifecycleStatus!=4&amp;&amp;qss-&gt;currentBusyWorkers==0&amp;&amp;qss-&gt;workerCounter&gt;qss-&gt;minThreads){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;workerCounter--;//until qss-&gt;workerCounter decrease to qss-&gt;minThreads<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exitCode=3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}//end GetQueuedCompletionStatus.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;if(<strong>SOErrOccurred</strong>){&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(qss-&gt;cslifecb)<br>&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;cslifecb(qssOl-&gt;client_s,-1);<br>&nbsp;&nbsp;&nbsp;/*if(qssOl)*/{<br>&nbsp;&nbsp;&nbsp;&nbsp;closesocket(qssOl-&gt;client_s);<br>&nbsp;&nbsp;&nbsp;&nbsp;free(qssOl);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;if(InterlockedDecrement(&amp;qss-&gt;<strong>CSocketsCounter</strong>)==0&amp;&amp;qss-&gt;lifecycleStatus&gt;=3){&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;//for qss workerSize,PostQueuedCompletionStatus -1<br>&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;lifecycleStatus=4,PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;exitCode=4;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<strong>qssOl=NULL,numberOfBytesTransferred=0,completionKey=0,SOErrOccurred=0;//for net while.<br></strong>&nbsp;}//end while.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;//last to do&nbsp;<br>&nbsp;if(exitCode!=3){&nbsp;<br>&nbsp;&nbsp;int clearup=0;<br>&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(!--qss-&gt;workerCounter&amp;&amp;qss-&gt;lifecycleStatus&gt;=4){//clearup QSS<br>&nbsp;&nbsp;&nbsp;&nbsp;clearup=1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(clearup){<br>&nbsp;&nbsp;&nbsp;DeleteCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;CloseHandle(qss-&gt;iocpHandle);<br>&nbsp;&nbsp;&nbsp;free(qss);&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;CloseHandle(curThread);<br>&nbsp;return 1;<br>}<br>------------------------------------------------------------------------------------------------------------------------<br>&nbsp; &nbsp; 对于IOCP的LastError的辨别和处理是个难点,所以请注意我的<strong>completionWorkerRoutine的while结构</strong>,<br>结构如下:<br>while(!exitCode){<br>&nbsp;&nbsp;&nbsp; if(<strong>completionKey==-1</strong>){...<strong>break</strong>;}<br>&nbsp;&nbsp;&nbsp; if(<strong>GetQueuedCompletionStatus</strong>){/*在这个if体中只要你投递的OVERLAPPED is not NULL,那么这里你得到的就是<strong>它</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(numberOfBytesTransferred&gt;0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*在这里handle request,<strong>记得要继续投递你的OVERLAPPED哦!</strong>&nbsp;*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*这里可能客户端或服务端closesocket(the socket),<strong>但是OVERLAPPED is not NULL,只要你投递的不为NULL!</strong>*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }else{/*在这里的if体中,虽然GetQueuedCompletionStatus return&nbsp;<strong>FALSE</strong>,但是不代表OVERLAPPED一定为NULL.<strong>特别是OVERLAPPED is not NULL的情况下,不要以为LastError发生了,就代表当前的socket无用或发生致命的异常,比如发生lastError:995这种情况下此时的socket有可能是一切正常的可用的,你不应该关闭它</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(OVERLAPPED is not NULL){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<strong>这种情况下,请不管37,21继续投递吧!在投递后再检测错误</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; if(<strong>socket error occured</strong>){<br><br>&nbsp; }<br>&nbsp; prepare for next while.<br>}&nbsp;<br></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>&nbsp;&nbsp;&nbsp; 行文仓促,难免有错误或不足之处,希望大家踊跃指正评论,谢谢!<br></strong></p><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong></strong></span><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong>&nbsp;&nbsp;&nbsp; 这个模型在性能上还是有改进的空间哦！</strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong><br></strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong>from:</strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong><a href="http://www.cppblog.com/adapterofcoms/archive/2010/06/26/118781.aspx">http://www.cppblog.com/adapterofcoms/archive/2010/06/26/118781.aspx</a></strong></strong></p>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/124731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-08-25 20:42 <a href="http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IOCP</title><link>http://www.cppblog.com/beautykingdom/archive/2010/05/05/114496.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 05 May 2010 07:51:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/05/05/114496.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/114496.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/05/05/114496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/114496.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/114496.html</trackback:ping><description><![CDATA[关于IOCP，先看微软的MSDN上讲的这一段：<br><a  href="http://msdn.microsoft.com/en-us/library/aa365198%28v=VS.85%29.aspx">http://msdn.microsoft.com/en-us/library/aa365198%28v=VS.85%29.aspx</a><br><br>另外，下面这个讲的也还是不错的。<br><a  href="http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html">http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html</a><br><br>
<h2>
<a  href="http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html" id="viewpost1_TitleUrl">IOCP
与线程</a>
</h2>
<p><font size="2">author : Kevin Lynx</font> </p>
<p><strong></strong>&nbsp; </p>
<p><strong>什么是完成包？</strong>  </p>
<p><font size="2">完成包，即IO Completion
Packet，是指异步IO操作完毕后OS提交给应用层的通知包。IOCP维护了一个IO操作结果队列，里面<br>保存着各种完成包。应用层调用
GQCS(也就是GetQueueCompletionStatus)函数获取这些完成包。 </font> </p>
<p><font size="2"><strong>最大并发线程数</strong> </font> </p>
<p><font size="2">在一个典型的
IOCP程序里，会有一些线程调用GQCS去获取IO操作结果。最大并发线程数指定在同一时刻处理完成包的线程数目。<br>该参数在调用
CreateIoCompletionPort时由NumberOfConcurrentThreads指定。 </font> </p>
<p><font size="2"><strong>工作者线程</strong> </font> </p>
<p><font size="2">工作者线程一般指的
就是调用GQCS函数的线程。要注意的是，工作者线程数和最大并发线程数并不是同一回事(见下文)。工作者<br>线程由应用层显示创建
(_beginthreadex 之类)。工作者线程通常是一个循环，会不断地GQCS到完成包，然后处理完成包。 </font> </p>
<p><font size="2"><strong>调度过程</strong> </font> </p>
<p><font size="2">工作者线程以是否阻塞
分为两种状态：运行状态和等待状态。当线程做一些阻塞操作时(线程同步，甚至GQCS空的完成队列)，线程<br>处于等待状态；否则，线程处于运行状
态。 </font> </p>
<p><font size="2">另一方面，OS会始终保持某一时刻处于运行状态的线程数小于最大并发线程数。每一个
调用GQCS函数的线程OS实际上都会进行记录，<br>当完成队列里有完成包时，OS会首先检查当前处于运行状态的工作线程数是否小于最大并发线程数，
如果小于，OS会按照LIFO的顺<br>序让某个工作者线程从GQCS返回(此工作者线程转换为运行状态)。如何决定这个LIFO？这是简单地通过调用
GQCS函数的顺序决定的。 </font> </p>
<p><font size="2">从这里可以看出，这里涉及到线程唤醒和睡眠的操作。如果两个
线程被放置于同一个CPU上，就会有线程切换的开销。因此，为了消<br>除这个开销，最大并发线程数被建议为设置成CPU数量。 </font> </p>
<p><font size="2">从以上调度过程还可以看出，如果某个处于运行状态的工作者线程在处理完成包时阻塞了(例如线程同步、其他IO操作)，那么就有<br>CPU
资源处于空闲状态。因此，我们也看到很多文档里建议，工作者线程数为(CPU数*2+2)。 </font> </p>
<p><font size="2">在一个等待线程转换到运行状态时，有可能会出现短暂的时间运行线程数超过最大并发线程数，这个时候OS会迅速地让这个新转换<br>的
线程阻塞，从而减少这个数量。(关于这个观点，MSDN上只说：by not allowing any new active
threads，却没说明not allowing<br>what) </font> </p>
<p><font size="2"><strong>调
度原理</strong> </font> </p>
<p><font size="2">这个知道了其实没什么意义，都是内核做的事，大致上都是操作线
程control block，直接摘录&lt;Inside IO Completion Ports&gt;: </font> </p>
<p><font size="2">The list of threads hangs off the queue object. A thread's
control block data structure has a pointer in it that <br>references the
queue object of a queue that it is associated with; if the pointer is
NULL then the thread is not <br>associated with a queue. </font> </p>
<p><font size="2">So how does NT keep track of threads that become inactive
because they block on something other than the completion<br>port" The
answer lies in the queue pointer in a thread's control block. The
scheduler routines that are executed <br>in response to a thread
blocking (KeWaitForSingleObject, KeDelayExecutionThread, etc.) check the
thread's queue <br>pointer and if its not NULL they will call
KiActivateWaiterQueue, a queue-related function. KiActivateWaiterQueue<br>
decrements the count of active threads associated with the queue, and
if the result is less than the maximum and <br>there is at least one
completion packet in the queue then the thread at the front of the
queue's thread list is <br>woken and given the oldest packet.
Conversely, whenever a thread that is associated with a queue wakes up
after <br>blocking the scheduler executes the function KiUnwaitThread,
which increments the queue's active count. </font> </p>
<p><font size="2"><strong>参
考资料</strong> </font> </p>
<p><font size="2">&lt;Inside I/O Completion
Ports&gt;:<br></font><a  href="http://technet.microsoft.com/en-us/sysinternals/bb963891.aspx"><font size="2">http://technet.microsoft.com/en-us/sysinternals/bb963891.aspx</font></a><br><font size="2">&lt;I/O Completion Ports&gt;:<br></font><a  href="http://msdn.microsoft.com/en-us/library/aa365198%28VS.85%29.aspx"><font size="2">http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx</font></a><br><font size="2">&lt;INFO: Design Issues When Using IOCP in a Winsock
Server&gt;:<br></font><a  href="http://support.microsoft.com/kb/192800/en-us/"><font size="2">http://support.microsoft.com/kb/192800/en-us/</font></a></p>
<br><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/114496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-05-05 15:51 <a href="http://www.cppblog.com/beautykingdom/archive/2010/05/05/114496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CALLBACK, WINAPI, AFXAPI和函数调用方式</title><link>http://www.cppblog.com/beautykingdom/archive/2010/04/28/113791.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 27 Apr 2010 16:10:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/04/28/113791.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/113791.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/04/28/113791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/113791.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/113791.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt;">(VC编译器下)</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>1. CALLBACK</strong><strong>，</strong><strong>WINAPI</strong><strong>和</strong><strong>AFXAPI</strong><strong>到底是什么？它们分别在什么地方
被定义的？</strong><strong></strong></p>
<p style="margin: 0cm 0cm 0pt;">在头文件windef.h中，CALLBACK,
WINAPI, APIENTRY</p>
<p style="margin: 0cm 0cm 0pt;">&#8230;&#8230;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>#define CALLBACK&nbsp; __stdcall</strong></p>
<p style="margin: 0cm 0cm 0pt;"><strong>#define WINAPI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
__stdcall</strong></p>
<p style="margin: 0cm 0cm 0pt;">#define WINAPIV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__cdecl</p>
<p style="margin: 0cm 0cm 0pt;"><strong>#define APIENTRY&nbsp;&nbsp;&nbsp;&nbsp;WINAPI</strong></p>
<p style="margin: 0cm 0cm 0pt;">&#8230;&#8230;</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">在头文件AFXVER_.H中，AFXAPI的定义如下：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; &#8230;&#8230;</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp; // AFXAPI is used on
global public functions</p>
<p style="margin: 0cm 0cm 0pt;">#ifndef AFXAPI</p>
<p style="margin: 0cm 0cm 0pt;"><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define
AFXAPI __stdcall</strong></p>
<p style="margin: 0cm 0cm 0pt;">#endif</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; &#8230;&#8230;</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>2. __stdcall</strong><strong>和</strong><strong>__cdecl</strong><strong>有什么作用？他们的区别是什么？</strong><strong></strong></p>
<p style="margin: 0cm 0cm 0pt;">a. __stdcall是新标准C/C++函数的调用方法。从底层上说，使用这种调用方法参数的进栈顺序和
标准C调用(__cdecl方法)是一样的,都</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;是从右到左,但是__stdcall采用自动清栈的方式，而__cdecl是手工清栈。</p>
<p style="margin: 0cm 0cm 0pt;"></p>
<p style="margin: 0cm 0cm 0pt;">b. windows规定,凡事由它来负责调用的函数必须定义为_stdcall类型，比如回调函数、WinMain函数等。</p>
<p style="margin: 0cm 0cm 0pt;"></p>
<p style="margin: 0cm 0cm 0pt;">c. 如果没有显试声明的话，函数的调用方法默认是__cdecl。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>3. </strong><strong>调用约定种类</strong><strong></strong></p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp; 一共有5中函数调用约定(calling
convention)，它决定一下内
容：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp; 1) 函数参数的压栈顺序</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp; 2) 由调用者还是被调用者把参数弹出栈</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp; 3) 产生函数修饰名的方法</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__stdcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">函数的参数自右向左通过栈传递，被调用的函数在返回前清理传送参数的内存栈，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__cdecl调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码，
所以产生的可执行文件大小会比调用__stdcall函数的大。函数采用从右到左的压栈方式。注意：对于可变参数的成员函数，始终使用__cdecl的转换方式。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__fastcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">它是通过寄存器来传送参数的(实际上，它用ECX和EDX传送前两个双字(DWORD)或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在
返回前清理传送参数的内存栈)。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">thiscall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">仅仅应用于"C++"成
员函数。this指
针存放于CX寄存
器，参数从右到左压。thiscall不是关键词，因此不能被程序员指定。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">naked call调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">采用上述4种调用约
定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符，故必须和_declspec共同使用。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">关键字 __stdcall、__cdecl 和 __fastcall 可以直接加在要输出的函数前，也可以在编译环境的 Setting...\C/C++ \Co<wbr>de
Generation 项选择。当加在
输出函数前的关键字与编译环境中的选择不同时，直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd 和 /Gr。缺省状态为/Gd，即__cdecl。缺省状态为__cdecl。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>4.
</strong><strong>名
字修饰约定</strong><strong></strong></p>
<p style="margin: 0cm 0cm 0pt;">"C" 或者 "C++" 函数在内部（编译和链接）通过修饰名识别。修饰名是编译器在编译
函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的，如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数，又如在汇编代码里调用"C""或"C++"函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同
决定。函数名修饰约定随编译种类(C或C++)和调用约定的不同而不同，下面分别说明。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">C编译时函数名修饰约定规则：</p>
<p style="margin: 0cm 0cm 0pt;">__stdcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp;
在输出函数名前加上一
个下划线前缀，后面加上一个"@"符号
和其参数的字节数，格式为 _functionname@number。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__cdecl调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;
&nbsp;仅在输出函数名前加上一个下划线前缀，格式为 _functionname。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__fastcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; 在输出函数名前加上一个"@"符号，后面也是一个"@"符号和其参数的字节数，格式为@functionname@number。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">它们均不改变输出函数名中的字符大小写。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">C++编译时函数名修饰约定规则：</p>
<p style="margin: 0cm 0cm 0pt;">__stdcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">以"?"标识
函数名的开始，后跟函数名；函数名后面以"@@YG"标识参数表的开始，后跟参数表；</p>
<p style="margin: 0cm 0cm 0pt;">参数表以代号表示：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; X——void，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; D——char，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; E——unsigned char，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; F——short，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; H——int，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; I——unsigned int，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; J——long，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; K——unsigned long，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; M——float，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; N——double，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; _N——bool，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; ....</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; PA——表示指针，后面的代号表明指针类型，如果相同类型的指针连续出现，
以"0"代替，一个"0"代表一次重复；</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前。</p>
<p style="margin: 0cm 0cm 0pt;">参数表后以"@Z"标识整个名字的结束，如果该函数无参数，则以"Z"标识结束。其格式为</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; "?functionname@@YG*****@Z"
或
"?functionname@@YG*XZ"，</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp;
例如</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; int Test1(char
*var1,unsigned long)&nbsp;&nbsp;&nbsp; -----&#8220;?Test1@@YGHPADK@Z&#8221;</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; void Test2()　　　　　　　　　　　&nbsp;&nbsp;
-----&#8220;?Test2@@YGXXZ&#8221;(第一个X表示返回类型，第二个X表示参数
类型)</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__cdecl调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp;
规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YA"。VC++对函数的省缺声明是"__cedcl"，将只能被C/C++调用。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">__fastcall调用约定：</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp;
规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YI"。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">对于C++的类成员函数（其调用方式是thiscall），函数的名字修饰与非成员的C++函数稍有不同，首先就是在函数名字和参数表之间插入以&#8220;@&#8221;字符引导的类名；其次是参数表的开始标识不同，公有（public）成员函数的标识是&#8220;@@QAE&#8221;,保护（protected）成员函数的标识是&#8220;@@IAE&#8221;,私有（private）成员函数的标识是&#8220;@@AAE&#8221;，如果函数声明使用了const关键字，则相应的标识应分别为&#8220;@@QBE&#8221;，&#8220;@@IBE&#8221;和&#8220;@@ABE&#8221;。如果参数类型是类实例的引用，则使用&#8220;AAV1&#8221;，对于const类型的引用，则使用&#8220;ABV1&#8221;。下面就以类CTest为例说明C++成员函数的名字修饰规则：</p>
<p style="margin: 0cm 0cm 0pt;">class CTest</p>
<p style="margin: 0cm 0cm 0pt;">{</p>
<p style="margin: 0cm 0cm 0pt;">......</p>
<p style="margin: 0cm 0cm 0pt;">private:</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp;
void Function(int);</p>
<p style="margin: 0cm 0cm 0pt;">protected:</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; void
CopyInfo(const CTest &amp;src);</p>
<p style="margin: 0cm 0cm 0pt;">public:</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;&nbsp;&nbsp; long
DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE
bUnder, bool bSet);</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;
&nbsp;&nbsp;long InsightClass(DWORD dwClass) const;</p>
<p style="margin: 0cm 0cm 0pt;">......</p>
<p style="margin: 0cm 0cm 0pt;">};</p>
<p style="margin: 0cm 0cm 0pt;">对于成员函数Function，其函数修饰名为&#8220;?Function@CTest@@AAEXH@Z&#8221;，字符串&#8220;@@AAE&#8221;表示这是一个私有函数。&#8220;X&#8221;表示返回类型为void，&#8220;H&#8221;表示参数类型为int类型。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">成员函数CopyInfo只有一个参数，是对类CTest的const引用参数，其函数修饰名为&#8220;?CopyInfo@CTest@@IAEXABV1@@Z&#8221;。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">DrawText是一个比较复杂的函数声明，不仅有字符串参数，还有结构体参数和HDC句柄参数，需要指出的是HDC实际上是一个HDC__结构类型的指针，这个参数的表示就是&#8220;PAUHDC__@@&#8221;，其完整的函数修饰名为&#8220;?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z&#8221;。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">InsightClass是一个共有的const函数，它的成员函数标识是&#8220;@@QBE&#8221;，完整的修饰名就是&#8220;?InsightClass@CTest@@QBEJK@Z&#8221;。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">举例：</p>
<p style="margin: 0cm 0cm 0pt;">比如动态链接库a有以下导出函数：</p>
<p style="margin: 0cm 0cm 0pt;">long MakeFun(long lFun);</p>
<p style="margin: 0cm 0cm 0pt;">动态库生成的时候采用的函数调用约定是__stdcall，所以编译生成的a.dll中函数MakeFun的调用约定是_stdcall，也就是函数调用时参数从右向左入栈，函数返回时自己还原堆栈。现在某个程序模块b要引用a中的MakeFun，b和a一样使用C++方式编译，只是b模块的函数调用方式是__cdecl，由于b包含了a提供的头文件中MakeFun函数声明，所以MakeFun在b模块中被
其它调用MakeFun的函数认为是__cdecl调用方式，b模块中的
这些函数在调用完MakeFun当然要帮着恢复堆栈啦，可是MakeFun已经在结束时自己恢复了堆栈，b模块中的函数这样多此一举就引起了栈指针错误，从而引发堆栈异常。宏观上的现象就是函数调用没有问题（因为参数传递
顺序是一样的），MakeFun也完成了自己的功能，只是函数返回后引发错误。解决的方法也很简单，只要保证两个模块的在编译时设置相同的函数调用约定就行了。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">现在再假定两个模块在编译的时候都采用__stdcall调用约定，但是a.dll使用C语言的语法编译的(C语言方式)，所以a.dll的
载入库a.lib中MakeFun函数的名字修饰就是&#8220;_MakeFun@4&#8221;。b包含了a提供的头文件中MakeFun函数声明，但是由于b采用的是C++语言编译，所以MakeFun在b模块中被按照C++的名字修饰规则命名为&#8220;?MakeFun@@YGJJ@Z&#8221;，编译过程相安无事，链接程序时c++的链接器就到a.lib中去找&#8220;?MakeFun@@YGJJ@Z&#8221;，但是a.lib中只有&#8220;_MakeFun@4&#8221;，没有&#8220;?MakeFun@@YGJJ@Z&#8221;，于是链接器就报告：</p>
<p style="margin: 0cm 0cm 0pt;">error LNK2001: unresolved external symbol
?MakeFun@@YGJJ@Z</p>
<p style="margin: 0cm 0cm 0pt;">解决的方法和简单，就是要让b模块知道这个函数是C语言编译的，extern "C"可以做到这一点。一个采用C语言编译的库应该考虑到使用这个库的程序可能是C++程序（使用C++编译器），所以在设计头文件时应该注意这一点。通常应该这样声明
头文件：</p>
<p style="margin: 0cm 0cm 0pt;">#ifdef
_cplusplus</p>
<p style="margin: 0cm 0cm 0pt;">extern "C" {</p>
<p style="margin: 0cm 0cm 0pt;">#endif</p>
<p style="margin: 0cm 0cm 0pt;">long MakeFun(long lFun);</p>
<p style="margin: 0cm 0cm 0pt;">#ifdef
_cplusplus</p>
<p style="margin: 0cm 0cm 0pt;">}</p>
<p style="margin: 0cm 0cm 0pt;">#endif</p>
<p style="margin: 0cm 0cm 0pt;">这样C++的编译器就知道MakeFun的修饰名是&#8220;_MakeFun@4&#8221;，就不会有链接错误了。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;">许多人不明白，为什么我使用的编译器都是VC的编译器还会产生&#8220;error LNK2001&#8221;错误？其实，VC的编译器会根据源文件的扩展名选择编译方式，如果文件的扩展名是&#8220;.C&#8221;，编译器会采用C的语法编译，如果扩展名是&#8220;.cpp&#8221;，编译器会使用C++的语法编译程序，所以，最好的方法就是使用extern "C"。</p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong>5. </strong><strong>单看函数的名字修饰</strong><strong></strong></p>
有两种方式可以检查你的程序中的函数的名字修饰：使用编译输出列表或使用Dumpbin工具。使用/FAc，/FAs或/FAcs命令行参数可以让编译器输出函数或变量名字列表。使用dumpbin.exe
/SYMBOLS命令也可以获得obj文件或lib文件中的函数或变量名字列表。此外，还可以使用 undname.exe 将修饰名转换为未修饰形式。
<br><br>from:<br><a  href="http://patmusing.blog.163.com/blog/static/13583496020103233446784/">http://patmusing.blog.163.com/blog/static/13583496020103233446784/
</a><br><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/113791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-04-28 00:10 <a href="http://www.cppblog.com/beautykingdom/archive/2010/04/28/113791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>An In-Depth Look into the Win32 Portable Executable File Format</title><link>http://www.cppblog.com/beautykingdom/archive/2010/03/25/110504.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 25 Mar 2010 07:17:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/03/25/110504.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/110504.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/03/25/110504.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/110504.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/110504.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://msdn.microsoft.com/zh-cn/magazine/cc301808%28en-us%29.aspxDownload the code for this article: PE.exe(98KB)part1:SUMMARYA good understanding of the Portable Executable (PE) fi...&nbsp;&nbsp;<a href='http://www.cppblog.com/beautykingdom/archive/2010/03/25/110504.html'>阅读全文</a><img src ="http://www.cppblog.com/beautykingdom/aggbug/110504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-03-25 15:17 <a href="http://www.cppblog.com/beautykingdom/archive/2010/03/25/110504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>异步IO、APC、IO完成端口、线程池与高性能服务器</title><link>http://www.cppblog.com/beautykingdom/archive/2010/01/04/104762.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 04 Jan 2010 07:21:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/01/04/104762.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/104762.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/01/04/104762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/104762.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/104762.html</trackback:ping><description><![CDATA[
<span style="border-collapse: collapse; color: rgb(2, 54, 141); font-family: song, Verdana; font-size: 19px; font-weight: bold; ">异步IO、APC、IO完成端口、线程池与高性能服务器</span>

<div><font color="#02368D" face="song, Verdana" size="6"><span style="border-collapse: collapse; font-size: 19px;"><strong><div>异步IO、APC、IO完成端口、线程池与高性能服务器之一 异步IO</div><div><br></div><div>背景：轮询 PIO DMA 中断</div><div><br></div><div>&nbsp;&nbsp; &nbsp;早期IO设备的速度与CPU相比，还不是太悬殊。CPU定时轮询一遍IO设备，看看有无处理要求，有则加以处理，完成后返回继续工作。至今，软盘驱动器还保留着这种轮询工作方式。</div><div>&nbsp;&nbsp; &nbsp;随 着CPU性能的迅速提高，这种效率低下的工作方式浪费了大量的CPU时间。因此，中断工作方式开始成为普遍采用的技术。这种技术使得IO设备在需要得到服 务时，能够产生一个硬件中断，迫使CPU放弃目前的处理任务，进入特定的中断服务过程，中断服务完成后，再继续原先的处理。这样一来，IO设备和CPU可 以同时进行处理，从而避免了CPU等待IO完成。</div><div>&nbsp;&nbsp; &nbsp;早期数据的传输方式主要是PIO（程控IO）方式。通过对IO地址编程方式的方式来传输 数据。比如串行口，软件每次往串行口上写一个字节数据，串口设备完成传输任务后，将会产生一个中断，然后软件再次重复直到全部数据发送完成。性能更好的硬 件设备提供一个FIFO（先进先出缓冲部件），可以让软件一次传输更多的字节。</div><div>&nbsp;&nbsp; &nbsp;显然，使用PIO方式对于高速IO设备来说，还是占用了太 多的CPU时间（因为需要通过CPU编程控制传输）。而DMA（直接内存访问）方式能够极大地减少CPU处理时间。CPU仅需告诉DMA控制器数据块的起 始地址和大小，以后DMA控制器就可以自行在内存和设备之间传输数据，其间CPU可以处理其他任务。数据传输完毕后将会产生一个中断。</div><div><br></div><div>同步文件IO和异步文件IO</div><div><br></div><div>下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》。</div><div>有两种类型的文件IO同步：同步文件IO和异步文件IO。异步文件IO也就是重叠IO。</div><div>在同步文件IO中，线程启动一个IO操作然后就立即进入等待状态，直到IO操作完成后才醒来继续执行。而异步文件IO方式中，线程发送一个IO请求到内核，然后继续处理其他的事情，内核完成IO请求后，将会通知线程IO操作完成了。</div><div>&nbsp;</div><div>如果IO请求需要大量时间执行的话，异步文件IO方式可以显著提高效率，因为在线程等待的这段时间内，CPU将会调度其他线程进行执行，如果没 有其他线程需要执行的话，这段时间将会浪费掉（可能会调度操作系统的零页线程）。如果IO请求操作很快，用异步IO方式反而还低效，还不如用同步IO方 式。</div><div>&nbsp;&nbsp; &nbsp;同步IO在同一时刻只允许一个IO操作，也就是说对于同一个文件句柄的IO操作是序列化的，即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。</div><div>&nbsp;&nbsp; &nbsp;异步IO在请求完成时，通过将文件句柄设为有信号状态来通知应用程序，或者应用程序通过GetOverlappedResult察看IO请求是否完成，也可以通过一个事件对象来通知应用程序。</div><div><br></div><div>参考书目</div><div><br></div><div>1， &nbsp; &nbsp;MSDN Library&nbsp;</div><div>2， &nbsp; &nbsp;《Windows高级编程指南》</div><div>3， &nbsp; &nbsp;《Windows核心编程》</div><div>4， &nbsp; &nbsp;《Windows 2000 设备驱动程序设计指南》</div><div><br></div><div>异步IO、APC、IO完成端口、线程池与高性能服务器之二 APC</div><div><br></div><div>&nbsp;&nbsp; &nbsp;Alertable IO(告警IO)提供了更有效的异步通知形式。ReadFileEx / WriteFileEx在发出IO请求的同时，提供一个回调函数（APC过程），当IO请求完成后，一旦线程进入可告警状态，回调函数将会执行。</div><div>&nbsp;&nbsp; &nbsp;以下五个函数能够使线程进入告警状态：</div><div>&nbsp;&nbsp; &nbsp;SleepEx</div><div>&nbsp;&nbsp; &nbsp;WaitForSingleObjectEx</div><div>&nbsp;&nbsp; &nbsp;WaitForMultipleObjectsEx</div><div>&nbsp;&nbsp; &nbsp;SignalObjectAndWait</div><div>&nbsp;&nbsp; &nbsp;MsgWaitForMultipleObjectsEx</div><div>&nbsp;&nbsp; &nbsp;线 程进入告警状态时，内核将会检查线程的APC队列，如果队列中有APC，将会按FIFO方式依次执行。如果队列为空，线程将会挂起等待事件对象。以后的某 个时刻，一旦APC进入队列，线程将会被唤醒执行APC，同时等待函数返回WAIT_IO_COMPLETION。</div><div>&nbsp;&nbsp; &nbsp;QueueUserAPC可以用来人为投递APC，只要目标线程处于告警状态时，APC就能够得到执行。</div><div>&nbsp;&nbsp; &nbsp;使用告警IO的主要缺点是发出IO请求的线程也必须是处理结果的线程，如果一个线程退出时还有未完成的IO请求，那么应用程序将永远丢失IO完成通知。然而以后我们将会看到IO完成端口没有这个限制。</div><div>&nbsp;&nbsp; &nbsp;下面的代码演示了QueueUserAPC的用法。</div><div><br></div><div>/************************************************************************/</div><div>/* APC Test. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div><div>/************************************************************************/</div><div><br></div><div>DWORD WINAPI WorkThread(PVOID pParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;HANDLE Event = (HANDLE)pParam;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(;;)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;DWORD dwRet = WaitForSingleObjectEx(Event, INFINITE, TRUE);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if(dwRet == WAIT_OBJECT_0)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;else if(dwRet == WAIT_IO_COMPLETION)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("WAIT_IO_COMPLETION\n");</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return 0;</div><div>}</div><div><br></div><div>VOID CALLBACK APCProc(DWORD dwParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;printf("%s", (PVOID)dwParam);</div><div>}</div><div><br></div><div>void TestAPC(BOOL bFast)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;HANDLE QuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;HANDLE hThread = CreateThread(NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;0,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WorkThread,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(PVOID)QuitEvent,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;0,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;NULL);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;Sleep(100); // Wait for WorkThread initialized.</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(int i=5; i&gt;0; i--)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;QueueUserAPC(APCProc, hThread, (DWORD)(PVOID)"APC here\n");</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if(!bFast)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Sleep(1000);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;SetEvent(QuitEvent);</div><div>&nbsp;&nbsp; &nbsp;WaitForSingleObject(hThread, INFINITE);</div><div>&nbsp;&nbsp; &nbsp;CloseHandle(hThread);</div><div>}</div><div><br></div><div><br></div><div>参考书目</div><div><br></div><div>1， &nbsp; &nbsp;MSDN Library&nbsp;</div><div>2， &nbsp; &nbsp;《Windows高级编程指南》</div><div>3， &nbsp; &nbsp;《Windows核心编程》</div><div>4， &nbsp; &nbsp;《Windows 2000 设备驱动程序设计指南》</div><div><br></div><div><br></div><div>异步IO、APC、IO完成端口、线程池与高性能服务器之三 IO完成端口</div><div><br></div><div>IO完成端口</div><div><br></div><div>下面摘抄于MSDN《I/O Completion Ports》，smallfool翻译，原文请参考CSDN文档中心文章《I/O Completion Ports》， http://dev.csdn.net/Develop/article/29%5C29240.shtm 。</div><div>I/O 完成端口是一种机制，通过这个机制，应用程序在启动时会首先创建一个线程池，然后该应用程序使用线程池处理异步I/O请求。这些线程被创建的唯一目的就是 用于处理I/O请求。对于处理大量并发异步I/O请求的应用程序来说，相比于在I/O请求发生时创建线程来说，使用完成端口(s)它就可以做的更快且更有 效率。</div><div>CreateIoCompletionPort函数会使一个I/O完成端口与一个或多个文件句柄发生关联。当与一个完成端口相关的文件句柄 上启动的异步I/O操作完成时，一个I/O完成包就会进入到该完成端口的队列中。对于多个文件句柄来说，这种机制可以用来把多文件句柄的同步点放在单个对 象中。（言下之意，如果我们需要对每个句柄文件进行同步，一般而言我们需要多个对象（如：Event来同步），而我们使用IO Complete Port 来实现异步操作，我们可以同多个文件相关联，每当一个文件中的异步操作完成，就会把一个complete package放到队列中，这样我们就可以使用这个来完成所有文件句柄的同步）</div><div>调用GetQueuedCompletionStatus函数，某 个线程就会等待一个完成包进入到完成端口的队列中，而不是直接等待异步I/O请求完成。线程（们）就会阻塞于它们的运行在完成端口（按照后进先出队列顺序 的被释放）。这就意味着当一个完成包进入到完成端口的队列中时，系统会释放最近被阻塞在该完成端口的线程。</div><div>调用GetQueuedCompletionStatus，线程就会将会与某个指定的完成端口建立联系，一直延续其该线程的存在周期，或被指定了不同的完成端口，或者释放了与完成端口的联系。一个线程只能与最多不超过一个的完成端口发生联系。</div><div>完 成端口最重要的特性就是并发量。完成端口的并发量可以在创建该完成端口时指定。该并发量限制了与该完成端口相关联的可运行线程的数目。当与该完成端口相关 联的可运行线程的总数目达到了该并发量，系统就会阻塞任何与该完成端口相关联的后续线程的执行，直到与该完成端口相关联的可运行线程数目下降到小于该并发 量为止。最有效的假想是发生在有完成包在队列中等待，而没有等待被满足，因为此时完成端口达到了其并发量的极限。此时，一个正在运行中的线程调用 GetQueuedCompletionStatus时，它就会立刻从队列中取走该完成包。这样就不存在着环境的切换，因为该处于运行中的线程就会连续不 断地从队列中取走完成包，而其他的线程就不能运行了。</div><div>对于并发量最好的挑选值就是您计算机中CPU的数目。如果您的事务处理需要一个漫长的计算时间，一个比较大的并发量可以允许更多线程来运行。虽然完成每个事务处理需要花费更长的时间，但更多的事务可以同时被处理。对于应用程序来说，很容易通过测试并发量来获得最好的效果。</div><div>PostQueuedCompletionStatus函数允许应用程序可以针对自定义的专用I/O完成包进行排队，而无需启动一个异步I/O操作。这点对于通知外部事件的工作者线程来说很有用。</div><div>在没有更多的引用针对某个完成端口时，需要释放该完成端口。该完成端口句柄以及与该完成端口相关联的所有文件句柄都需要被释放。调用CloseHandle可以释放完成端口的句柄。</div><div><br></div><div>下面的代码利用IO完成端口做了一个简单的线程池。</div><div><br></div><div>/************************************************************************/</div><div>/* Test IOCompletePort. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */</div><div>/************************************************************************/</div><div><br></div><div>DWORD WINAPI IOCPWorkThread(PVOID pParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;HANDLE CompletePort = (HANDLE)pParam;</div><div>&nbsp;&nbsp; &nbsp;PVOID UserParam;</div><div>&nbsp;&nbsp; &nbsp;WORK_ITEM_PROC UserProc;</div><div>&nbsp;&nbsp; &nbsp;LPOVERLAPPED pOverlapped;</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;for(;;)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;BOOL bRet = GetQueuedCompletionStatus(</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CompletePort,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(LPDWORD)&amp;UserParam,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(LPDWORD)&amp;UserProc,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&amp;pOverlapped,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;INFINITE);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;_ASSERT(bRet);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if(UserProc == NULL) // Quit signal.</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;// execute user's proc. &nbsp; &nbsp; &nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;UserProc(UserParam); &nbsp; &nbsp; &nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return 0;</div><div>}</div><div><br></div><div>void TestIOCompletePort(BOOL bWaitMode, LONG ThreadNum)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;HANDLE CompletePort;</div><div>&nbsp;&nbsp; &nbsp;OVERLAPPED Overlapped = {0, 0, 0, 0, NULL};</div><div><br></div><div>&nbsp;&nbsp; &nbsp;CompletePort = CreateIoCompletionPort(</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;INVALID_HANDLE_VALUE,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;0);</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;// Create threads.</div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;ThreadNum; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;HANDLE hThread = CreateThread(NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IOCPWorkThread,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CompletePort,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NULL);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;CloseHandle(hThread);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div><br></div><div>&nbsp;&nbsp; &nbsp;CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>&nbsp;&nbsp; &nbsp;BeginTime = GetTickCount();</div><div>&nbsp;&nbsp; &nbsp;ItemCount = 20;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(i=0; i&lt;20; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;PostQueuedCompletionStatus(</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CompletePort,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(DWORD)bWaitMode,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(DWORD)UserProc1,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&amp;Overlapped);</div><div>&nbsp;&nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;WaitForSingleObject(CompleteEvent, INFINITE);</div><div>&nbsp;&nbsp; &nbsp;CloseHandle(CompleteEvent);</div><div><br></div><div><br></div><div>&nbsp;&nbsp; &nbsp;// Destroy all threads.</div><div>&nbsp;&nbsp; &nbsp;for(i=0; i&lt;ThreadNum; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;PostQueuedCompletionStatus(</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CompletePort,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NULL,</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&amp;Overlapped);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;Sleep(1000); // wait all thread exit.</div><div><br></div><div>&nbsp;&nbsp; &nbsp;CloseHandle(CompletePort);</div><div>}</div><div><br></div><div><br></div><div>参考书目</div><div><br></div><div>1， &nbsp; &nbsp;MSDN Library&nbsp;</div><div>2， &nbsp; &nbsp;《Windows高级编程指南》</div><div>3， &nbsp; &nbsp;《Windows核心编程》</div><div>4， &nbsp; &nbsp;《Windows 2000 设备驱动程序设计指南》</div><div><br></div><div><br></div><div>异步IO、APC、IO完成端口、线程池与高性能服务器之四 线程池</div><div><br></div><div>线程池</div><div><br></div><div>下面摘抄于MSDN《Thread Pooling》。</div><div>有 许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池 可以让你更有效地使用线程，它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作，当等待操作完成后，线 程池中将会有一个工作者线程来执行相应的回调函数。</div><div>你也可以把没有等待操作的工作项目放到线程池中，用QueueUserWorkItem函数来完成这个工作，把要执行的工作项目函数通过一个参数传递给线程池。工作项目被放到线程池中后，就不能再取消了。</div><div>Timer-queue timers和Registered wait operations也使用线程池来实现。他们的回调函数也放在线程池中。你也可以用BindIOCompletionCallback函数来投递一个异 步IO操作，在IO完成端口上，回调函数也是由线程池线程来执行。</div><div>当第一次调用QueueUserWorkItem函数或者 BindIOCompletionCallback函数的时候，线程池被自动创建，或者Timer-queue timers或者Registered wait operations放入回调函数的时候，线程池也可以被创建。线程池可以创建的线程数量不限，仅受限于可用的内存，每一个线程使用默认的初始堆栈大小， 运行在默认的优先级上。</div><div>线程池中有两种类型的线程：IO线程和非IO线程。IO线程等待在可告警状态，工作项目作为APC放到IO线程中。如果你的工作项目需要线程执行在可警告状态，你应该将它放到IO线程。</div><div>非IO工作者线程等待在IO完成端口上，使用非IO线程比IO线程效率更高，也就是说，只要有可能的话，尽量使用非IO线程。IO线程和非IO线程在异步IO操作没有完成之前都不会退出。然而，不要在非IO线程中发出需要很长时间才能完成的异步IO请求。</div><div>正 确使用线程池的方法是，工作项目函数以及它将会调用到的所有函数都必须是线程池安全的。安全的函数不应该假设线程是一次性线程的或者是永久线程。一般来 说，应该避免使用线程本地存储和发出需要永久线程的异步IO调用，比如说RegNotifyChangeKeyValue函数。如果需要在永久线程中执行 这样的函数的话，可以给QueueUserWorkItem传递一个选项WT_EXECUTEINPERSISTENTTHREAD。</div><div>注意，线程池不能兼容COM的单线程套间（STA）模型。</div><div><br></div><div>&nbsp;&nbsp; &nbsp;为了更深入地讲解操作系统实现的线程池的优越性，我们首先尝试着自己实现一个简单的线程池模型。</div><div><br></div><div>&nbsp;&nbsp; &nbsp;代码如下：</div><div><br></div><div>/************************************************************************/</div><div>/* Test Our own thread pool. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div><div>/************************************************************************/</div><div><br></div><div>typedef struct _THREAD_POOL</div><div>{</div><div>&nbsp;&nbsp; &nbsp;HANDLE QuitEvent;</div><div>&nbsp;&nbsp; &nbsp;HANDLE WorkItemSemaphore;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;LONG WorkItemCount;</div><div>&nbsp;&nbsp; &nbsp;LIST_ENTRY WorkItemHeader;</div><div>&nbsp;&nbsp; &nbsp;CRITICAL_SECTION WorkItemLock;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;LONG ThreadNum;</div><div>&nbsp;&nbsp; &nbsp;HANDLE *ThreadsArray;</div><div><br></div><div>}THREAD_POOL, *PTHREAD_POOL;</div><div><br></div><div>typedef VOID (*WORK_ITEM_PROC)(PVOID Param);</div><div><br></div><div>typedef struct _WORK_ITEM</div><div>{</div><div>&nbsp;&nbsp; &nbsp;LIST_ENTRY List;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;WORK_ITEM_PROC UserProc;</div><div>&nbsp;&nbsp; &nbsp;PVOID UserParam;</div><div>&nbsp;&nbsp; &nbsp;</div><div>}WORK_ITEM, *PWORK_ITEM;</div><div><br></div><div><br></div><div>DWORD WINAPI WorkerThread(PVOID pParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam;</div><div>&nbsp;&nbsp; &nbsp;HANDLE Events[2];</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;Events[0] = pThreadPool-&gt;QuitEvent;</div><div>&nbsp;&nbsp; &nbsp;Events[1] = pThreadPool-&gt;WorkItemSemaphore;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(;;)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if(dwRet == WAIT_OBJECT_0)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;//</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;// execute user's proc.</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;//</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;else if(dwRet == WAIT_OBJECT_0 +1)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PWORK_ITEM pWorkItem;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PLIST_ENTRY pList;</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;EnterCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_ASSERT(!IsListEmpty(&amp;pThreadPool-&gt;WorkItemHeader));</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pList = RemoveHeadList(&amp;pThreadPool-&gt;WorkItemHeader);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;LeaveCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pWorkItem-&gt;UserProc(pWorkItem-&gt;UserParam);</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;InterlockedDecrement(&amp;pThreadPool-&gt;WorkItemCount);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;free(pWorkItem);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;else</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_ASSERT(0);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return 0;</div><div>}</div><div><br></div><div>BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;pThreadPool-&gt;QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);</div><div>&nbsp;&nbsp; &nbsp;pThreadPool-&gt;WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);</div><div>&nbsp;&nbsp; &nbsp;pThreadPool-&gt;WorkItemCount = 0;</div><div>&nbsp;&nbsp; &nbsp;InitializeListHead(&amp;pThreadPool-&gt;WorkItemHeader);</div><div>&nbsp;&nbsp; &nbsp;InitializeCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div>&nbsp;&nbsp; &nbsp;pThreadPool-&gt;ThreadNum = ThreadNum;</div><div>&nbsp;&nbsp; &nbsp;pThreadPool-&gt;ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;ThreadNum; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;pThreadPool-&gt;ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return TRUE;</div><div>}</div><div><br></div><div>VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;SetEvent(pThreadPool-&gt;QuitEvent);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;pThreadPool-&gt;ThreadNum; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WaitForSingleObject(pThreadPool-&gt;ThreadsArray[i], INFINITE);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;CloseHandle(pThreadPool-&gt;ThreadsArray[i]);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;free(pThreadPool-&gt;ThreadsArray);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;CloseHandle(pThreadPool-&gt;QuitEvent);</div><div>&nbsp;&nbsp; &nbsp;CloseHandle(pThreadPool-&gt;WorkItemSemaphore);</div><div>&nbsp;&nbsp; &nbsp;DeleteCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;while(!IsListEmpty(&amp;pThreadPool-&gt;WorkItemHeader))</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;PWORK_ITEM pWorkItem;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;PLIST_ENTRY pList;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;pList = RemoveHeadList(&amp;pThreadPool-&gt;WorkItemHeader);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;free(pWorkItem);</div><div>&nbsp;&nbsp; &nbsp;}</div><div>}</div><div><br></div><div>BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM));</div><div>&nbsp;&nbsp; &nbsp;if(pWorkItem == NULL)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return FALSE;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;pWorkItem-&gt;UserProc = UserProc;</div><div>&nbsp;&nbsp; &nbsp;pWorkItem-&gt;UserParam = UserParam;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;EnterCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div>&nbsp;&nbsp; &nbsp;InsertTailList(&amp;pThreadPool-&gt;WorkItemHeader, &amp;pWorkItem-&gt;List);</div><div>&nbsp;&nbsp; &nbsp;LeaveCriticalSection(&amp;pThreadPool-&gt;WorkItemLock);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;InterlockedIncrement(&amp;pThreadPool-&gt;WorkItemCount);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;ReleaseSemaphore(pThreadPool-&gt;WorkItemSemaphore, 1, NULL);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return TRUE;</div><div>}</div><div><br></div><div>VOID UserProc1(PVOID dwParam)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;WorkItem(dwParam);</div><div>}</div><div><br></div><div>void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;THREAD_POOL ThreadPool; &nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;InitializeThreadPool(&amp;ThreadPool, ThreadNum);</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>&nbsp;&nbsp; &nbsp;BeginTime = GetTickCount();</div><div>&nbsp;&nbsp; &nbsp;ItemCount = 20;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;20; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;PostWorkItem(&amp;ThreadPool, UserProc1, (PVOID)bWaitMode);</div><div>&nbsp;&nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;WaitForSingleObject(CompleteEvent, INFINITE);</div><div>&nbsp;&nbsp; &nbsp;CloseHandle(CompleteEvent);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;DestroyThreadPool(&amp;ThreadPool);</div><div>}</div><div>&nbsp;&nbsp; &nbsp;我们把工作项目放到一个队列中，用一个信号量通知线程池，线程池中任意一个线程取出工作项目来执行，执行完毕之后，线程返回线程池，继续等待新的工作项目。</div><div>&nbsp;&nbsp; &nbsp;线程池中线程的数量是固定的，预先创建好的，永久的线程，直到销毁线程池的时候，这些线程才会被销毁。</div><div>&nbsp;&nbsp; &nbsp;线程池中线程获得工作项目的机会是均等的，随机的，并没有特别的方式保证哪一个线程具有特殊的优先获得工作项目的机会。</div><div>&nbsp;&nbsp; &nbsp;而且，同一时刻可以并发运行的线程数目没有任何限定。事实上，在我们的执行计算任务的演示代码中，所有的线程都并发执行。</div><div>&nbsp;&nbsp; &nbsp;下面，我们再来看一下，完成同样的任务，系统提供的线程池是如何运作的。</div><div><br></div><div>/************************************************************************/</div><div>/* QueueWorkItem Test. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div><div>/************************************************************************/</div><div><br></div><div>DWORD BeginTime;</div><div>LONG &nbsp;ItemCount;</div><div>HANDLE CompleteEvent;</div><div><br></div><div>int compute()</div><div>{</div><div>&nbsp;&nbsp; &nbsp;srand(BeginTime);</div><div><br></div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;20 *1000 * 1000; i++)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;rand();</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return rand();</div><div>}</div><div><br></div><div>DWORD WINAPI WorkItem(LPVOID lpParameter)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;BOOL bWaitMode = (BOOL)lpParameter;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;if(bWaitMode)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Sleep(1000);</div><div>&nbsp;&nbsp; &nbsp;else</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;compute();</div><div><br></div><div>&nbsp;&nbsp; &nbsp;if(InterlockedDecrement(&amp;ItemCount) == 0)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;printf("Time total %d second.\n", GetTickCount() - BeginTime);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;SetEvent(CompleteEvent);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br></div><div>&nbsp;&nbsp; &nbsp;return 0;</div><div>}</div><div><br></div><div>void TestWorkItem(BOOL bWaitMode, DWORD Flag)</div><div>{</div><div>&nbsp;&nbsp; &nbsp;CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>&nbsp;&nbsp; &nbsp;BeginTime = GetTickCount();</div><div>&nbsp;&nbsp; &nbsp;ItemCount = 20;</div><div>&nbsp;&nbsp; &nbsp;</div><div>&nbsp;&nbsp; &nbsp;for(int i=0; i&lt;20; i++)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);</div><div>&nbsp;&nbsp; &nbsp;} &nbsp; &nbsp;</div><div><br></div><div>&nbsp;&nbsp; &nbsp;WaitForSingleObject(CompleteEvent, INFINITE);</div><div>&nbsp;&nbsp; &nbsp;CloseHandle(CompleteEvent);</div><div>}</div><div>&nbsp;&nbsp; &nbsp;很简单，是吧？我们仅需要关注于我们的回调函数即可。但是与我们的简单模拟来比，系统提供的线程池有着更多的优点。</div><div>&nbsp;&nbsp; &nbsp;首先，线程池中线程的数目是动态调整的，其次，线程池利用IO完成端口的特性，它可以限制并发运行的线程数目，默认情况下，将会限制为CPU的数目，这可以减少线程切换。它挑选最近执行过的线程再次投入执行，从而避免了不必要的线程切换。</div><div>&nbsp;&nbsp; &nbsp;系统提供的线程池背后的策略，我们下一节继续再谈。</div><div><br></div><div>参考书目</div><div><br></div><div>1， &nbsp; &nbsp;MSDN Library&nbsp;</div><div>2， &nbsp; &nbsp;《Windows高级编程指南》</div><div>3， &nbsp; &nbsp;《Windows核心编程》</div><div>4， &nbsp; &nbsp;《Windows 2000 设备驱动程序设计指南》</div><div><br></div><div><br></div><div>正文</div><div>异步IO、APC、IO完成端口、线程池与高性能服务器之五 服务器的性能指标与实现高性能的途径</div><div><br></div><div>服务器的性能指标</div><div><br></div><div>&nbsp;&nbsp; &nbsp;作为一个网络服务器程序，性能永远是第一位的指标。性能可以这样定义：在给定的硬件条件和时间里，能够处理的任务量。能够最大限度地利用硬件性能的服务器设计才是良好的设计。</div><div>&nbsp;&nbsp; &nbsp;设计良好的服务器还应该考虑平均服务，对于每一个客户端，服务器应该给予每个客户端平均的服务，不能让某一个客户端长时间得不到服务而发生&#8220;饥饿&#8221;的状况。</div><div>&nbsp;&nbsp; &nbsp;可伸缩性，也就是说，随着硬件能力的提高，服务器的性能能够随之呈线性增长。</div><div><br></div><div>实现高性能的途径</div><div><br></div><div>&nbsp;&nbsp; &nbsp;一 个实际的服务器的计算是很复杂的，往往是混合了IO计算和CPU计算。IO计算指计算任务中以IO为主的计算模型，比如文件服务器、邮件服务器等，混合了 大量的网络IO和文件IO；CPU计算指计算任务中没有或很少有IO，比如加密/解密，编码/解码，数学计算等等。</div><div>&nbsp;&nbsp; &nbsp;在CPU计算中，单线 程和多线程模型效果是相当的。《Win32多线程的性能》中说&#8220;在一个单处理器的计算机中，基于 CPU 的任务的并发执行速度不可能比串行执行速度快，但是我们可以看到，在 Windows NT 下线程创建和切换的额外开销非常小；对于非常短的计算，并发执行仅仅比串行执行慢 10%，而随着计算长度的增加，这两个时间就非常接近了。&#8221;</div><div>&nbsp;&nbsp; &nbsp;可见，对于纯粹的CPU计算来说，如果只有一个CPU，多线程模型是不合适的。考虑一个执行密集的CPU计算的服务，如果有几十个这样的线程并发执行，过于频繁地任务切换导致了不必要的性能损失。</div><div>&nbsp;&nbsp; &nbsp;在 编程实现上，单线程模型计算模型对于服务器程序设计是很不方便的。因此，对于CPU计算采用线程池工作模型是比较恰当的。 QueueUserWorkItem函数非常适合于将一个CPU计算放入线程池。线程池实现将会努力减少这种不必要的线程切换，而且控制并发线程的数目为 CPU的数目。</div><div>&nbsp;&nbsp; &nbsp;我们真正需要关心的是IO计算，一般的网络服务器程序往往伴随着大量的IO计算。提高性能的途径在于要避免等待IO 的结束，造成CPU空闲，要尽量利用硬件能力，让一个或多个IO设备与CPU并发执行。前面介绍的异步IO，APC，IO完成端口都可以达到这个目的。</div><div>&nbsp;&nbsp; &nbsp;对 于网络服务器来说，如果客户端并发请求数目比较少的话，用简单的多线程模型就可以应付了。如果一个线程因为等待IO操作完成而被挂起，操作系统将会调度另 外一个就绪的线程投入运行，从而形成并发执行。经典的网络服务器逻辑大多采用多线程/多进程方式，在一个客户端发起到服务器的连接时，服务器将会创建一个 线程，让这个新的线程来处理后续事务。这种以一个专门的线程/进程来代表一个客户端对象的编程方法非常直观，易于理解。</div><div>&nbsp;&nbsp; &nbsp;对于大型网络服务 器程序来说，这种方式存在着局限性。首先，创建线程/进程和销毁线程/进程的代价非常高昂，尤其是在服务器采用TCP&#8220;短连接&#8221;方式或UDP方式通讯的情 况下，例如，HTTP协议中，客户端发起一个连接后，发送一个请求，服务器回应了这个请求后，连接也就被关闭了。如果采用经典方式设计HTTP服务器，那 么过于频繁地创建线程/销毁线程对性能造成的影响是很恶劣的。</div><div>&nbsp;&nbsp; &nbsp;其次，即使一个协议中采取TCP&#8220;长连接&#8221;，客户端连上服务器后就一直保持 此连接，经典的设计方式也是有弊病的。如果客户端并发请求量很高，同一时刻有很多客户端等待服务器响应的情况下，将会有过多的线程并发执行，频繁的线程切 换将用掉一部分计算能力。实际上，如果并发线程数目过多的话，往往会过早地耗尽物理内存，绝大部分时间耗费在线程切换上，因为线程切换的同时也将引起内存 调页。最终导致服务器性能急剧下降，</div><div>&nbsp;&nbsp; &nbsp;对于一个需要应付同时有大量客户端并发请求的网络服务器来说，线程池是唯一的解决方案。线程池不光能够避免频繁地创建线程和销毁线程，而且能够用数目很少的线程就可以处理大量客户端并发请求。</div><div>&nbsp;&nbsp; &nbsp;值得注意的是，对于一个压力不大的网络服务器程序设计，我们并不推荐以上任何技巧。在简单的设计就能够完成任务的情况下，把事情弄得很复杂是很不明智，很愚蠢的行为。</div><div>from:</div><div>http://blog.chinaunix.net/u2/67780/showart_2057137.html</div><div>原文地址 http://blog.chinaunix.net/u/14774/showart_88161.html</div></strong></span></font></div><img src ="http://www.cppblog.com/beautykingdom/aggbug/104762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-01-04 15:21 <a href="http://www.cppblog.com/beautykingdom/archive/2010/01/04/104762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> windows时间大全（zz）</title><link>http://www.cppblog.com/beautykingdom/archive/2009/02/06/73148.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Fri, 06 Feb 2009 15:32:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/02/06/73148.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/73148.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/02/06/73148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/73148.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/73148.html</trackback:ping><description><![CDATA[<div>介绍</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>我们在衡量一个函数运行时间，或者判断一个算法的时间效率，或者在程序中我们需要一个定时器，定时执行一个特定的操作，比如在多媒体中，比如在游戏中等，都会用到时间函数。还比如我们通过记录函数或者算法开始和截至的时间，然后利用两者之差得出函数或者算法的运行时间。编译器和操作系统为我们提供了很多时间函数，这些时间函数的精度也是各不相同的，所以，如果我们想得到准确的结果，必须使用合适的时间函数。现在我就介绍windows下的几种常用时间函数。</div>
<div><strong>1</strong><strong>：</strong><strong>Sleep</strong><strong>函数</strong></div>
<div style="TEXT-INDENT: 21pt"><strong>使用：</strong>sleep(1000)，在Windows和Linux下1000代表的含义并不相同，Windows下的表示1000毫秒，也就是1秒钟；Linux下表示1000秒，Linux下使用毫秒级别的函数可以使用usleep。</div>
<div style="TEXT-INDENT: 21pt"><strong>原理：</strong>sleep函数是使调用sleep函数的线程休眠，线程主动放弃时间片。当经过指定的时间间隔后，再启动线程，继续执行代码。Sleep函数并不能起到定时的作用，主要作用是延时。在一些多线程中可能会看到sleep(0);其主要目的是让出时间片。</div>
<div style="TEXT-INDENT: 21pt"><strong>精度：</strong>sleep函数的精度非常低，当系统越忙它精度也就越低，有时候我们休眠1秒，可能3秒后才能继续执行。它的精度取决于线程自身优先级、其他线程的优先级，以及线程的数量等因素。</div>
<div><strong>2</strong><strong>：</strong><strong>MFC</strong><strong>下的</strong><strong>timer</strong><strong>事件</strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>使用：</strong>1.调用函数SetTimer()设置定时间隔，如SetTimer(0,100,NULL)即为设置100毫秒的时间间隔；2.在应用程序中增加定时响应函数OnTimer()，并在该函数中添加响应的处理语句，用来完成时间到时的操作。</div>
<div><span>&nbsp;&nbsp;&nbsp; </span><strong>原理：</strong>同<span>sleep函数一样。不同的是timer是一个定时器，可以指定回调函数，默认为OnTimer()函数。</span></div>
<div><span>&nbsp;&nbsp;&nbsp; </span><strong>精度：</strong>timer事件的精度范围在毫米级别，系统越忙其精度也就越差。</div>
<div><strong>3</strong><strong>：</strong><strong>C</strong><strong>语言下的</strong><strong>Time</strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>使用：</strong>time_t t;time(&amp;t);Time函数是获取当前时间。</div>
<div><span>&nbsp;&nbsp;&nbsp; </span><strong>原理：</strong>time函数主要用于获取当前时间，比如我们做一个电子时钟程序，就可以使用此函数，获取系统当前的时间。</div>
<div><span>&nbsp;&nbsp;&nbsp; </span><strong>精度：</strong>秒级别</div>
<div><strong>4</strong><strong>：<span>COM对象中的</span></strong><strong>COleDateTime</strong><strong>，</strong><strong><span>COleDateTimeSpan</span><strong>类</strong></strong></div>
<div><strong><span>&nbsp;&nbsp;&nbsp; </span></strong><strong>使用：</strong>COleDateTime start_time = COleDateTime::GetCurrentTime();</div>
<div style="TEXT-INDENT: 21pt" align=left>COleDateTimeSpan end_time = COleDateTime::GetCurrentTime()-start_time;<br>While(end_time.GetTotalSeconds() &lt; 2)<br>{<br>// 处理延时或定时期间能处理其他的消息<br>DoSomething()<br>end_time = COleDateTime::GetCurrentTime-start_time;</div>
<div align=left>}</div>
<div style="TEXT-INDENT: 21pt" align=left><strong>原理：</strong>以上代表延时<span>2秒，而这两秒内我们可以循环调用DoSomething()，从而实现在</span><span>延时的时候我们也能够处理其他的函数，或者消息。</span><span>COleDateTime,COleDateTimeSpan</span><span>是</span><span>MFC</span><span>中</span><span>CTime</span><span>，</span><span>CTimeSpan</span><span>在</span><span>COM</span><span>中的应用，所以，上面的方法对于</span><span>CTime</span><span>，</span><span>CTimeSpa</span><span>同样有效。</span></div>
<div><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong>精度：</strong>秒级别</div>
<div><strong>5</strong><strong>：</strong><strong>C</strong><strong>语言下的时钟周期</strong><strong>clock()</strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>使用：</strong><span>&nbsp;&nbsp; </span><span style="FONT-SIZE: 9pt">clock_t start = clock();<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep(100);<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clock_t end = clock();<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">double</span> d = (<span style="COLOR: blue">double</span>)(start - end) / CLOCKS_PER_SEC;</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>原理：</strong>clock()是获取计算机启动后的时间间隔。 </div>
<div style="TEXT-INDENT: 21pt"><strong>精度：</strong>ms级别，对于短时间内的定时或者延时可以达到ms级别，对于时间比较长的定时或者延迟精度还是不够。在windows下CLOCKS_PER_SEC为<span>1000。</span></div>
<div><strong>6</strong><strong>：</strong><strong>Windows</strong><strong>下的</strong><strong>GetTickCount()</strong></div>
<div align=left><strong>使用：</strong>&nbsp;DWORD start = GetTickCount();<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep(100);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD end = GetTickCount();</span></div>
<div style="MARGIN-LEFT: 21pt"><strong>原理：</strong>GetTickCount()是获取系统启动后的时间间隔。通过进入函数开始定时，到退出函数结束定时，从而可以判断出函数的执行时间，这种时间也并非是函数或者算法的真实执行时间，因为在函数和算法线程不可能一直占用<span>CPU，</span><span>对于所有判断执行时间的函数都是一样，</span>不过基本上已经很准确，<span>可以通过查询进行定时。</span><span>GetTickCount()</span><span>和</span><span>Clock()</span><span>函数是向主板</span><span>BIOS</span><span>要</span><span>real time clock</span><span>时间，会有中断产生，以及延迟问题。</span></div>
<div style="MARGIN-LEFT: 21pt"><strong>精度：</strong>WindowsNT 3.5以及以后版本精度是<span>10ms，它的时间精度比clock函数的要高，GetTickCount()常用于多媒体中。</span></div>
<div><strong>7</strong><strong>：</strong><strong>Windows</strong><strong>下<span>timeGetTime</span></strong></div>
<div style="TEXT-INDENT: 21pt"><strong><span>使用：</span><span>需要包含</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Mmsystem.h</span><span style="FONT-SIZE: 8.5pt; COLOR: black">，</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Windows.h</span><span style="FONT-SIZE: 8.5pt; COLOR: black">，加入静态库</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Winmm.lib.</span></strong></div>
<div style="MARGIN-LEFT: 42pt; TEXT-INDENT: 21pt">timeBeginPeriod(1);<br><span style="FONT-SIZE: 9pt">DWORD start = timeGetTime();<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep(100);<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; DWORD end = timeGetTime();</span><br>timeEndPeriod(1);<strong><span></span></strong></div>
<div style="TEXT-INDENT: 21pt"><strong><span>原理：</span><span>timeGetTime</span><span>也时常用于多媒体定时器中，可以通过查询进行定时。通过查询进行定时，本身也会影响定时器的定时精度。</span></strong></div>
<div style="TEXT-INDENT: 21pt"><strong><span>精度：</span><span>毫秒</span>，与GetTickCount()相当。但是和GetTickCount相比，timeGetTime可以通过timeBeginPeriod，<span>timeEndPeriod设置定时器的最小解析精度, timeBeginPeriod,timeEndPeriod必须成对出现。</span></strong></div>
<div><strong>8</strong><strong>：<span>windows下的</span></strong><strong>timeSetEvent</strong></div>
<div style="TEXT-INDENT: 21pt">使用：还记的VC下的Timer吗？Timer是一个定时器，而以上我们提到几种时间函数或者类型，实现定时功能只能通过轮训来实现，也就是必须另外创建一个线程单独处理，这样会影响定时精度，好在windows提供了内置的定时器timeSetEvent，函数原型为</div>
<div><span>MMRESULT timeSetEvent</span><span>（ UINT uDelay, //</span><span>以毫秒指定事件的周期</span><br><span>UINT uResolution, //</span><span>以毫秒指定延时的精度，数值越小定时器事件分辨率越高。缺省值为1ms</span><br><span>LPTIMECALLBACK lpTimeProc, //</span><span>指向一个回调函数</span><br><span>WORD dwUser, //</span><span>存放用户提供的回调数据</span><br><span>UINT fuEvent </span><span>）// </span>标志参数，TIME_ONESHOT：执行一次；TIME_PERIODIC：周期性执行</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>具体应用时，可以通过调用timeSetEvent()函数，将需要周期性执行的任务定义在 lpFunction回调函数中(如：定时采样、控制等)，从而完成所需处理的事件。需要注意的是：任务处理的时间不能大于周期间隔时间。另外，在定时器使用完毕后，应及时调用timeKillEvent()将之释放。</div>
<div style="TEXT-INDENT: 21pt"><strong>原理：</strong>可以理解为代回调函数的timeGetTime</div>
<div style="TEXT-INDENT: 21pt"><strong>精度：</strong><span>毫秒</span>，timeSetEvent可以通过timeBeginPeriod，<span>timeEndPeriod设置定时器的最小解析精度, timeBeginPeriod,timeEndPeriod必须成对出现。</span><br><strong>9</strong><strong>：高精度时控函数</strong><strong>QueryPerformanceFrequency</strong><strong>，</strong><strong>QueryPerformanceCounter</strong></div>
<div style="TEXT-INDENT: 21pt"><strong>使用：</strong><span style="FONT-SIZE: 9pt">LARGE_INTEGER m_nFreq;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; LARGE_INTEGER m_nBeginTime;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; LARGE_INTEGER nEndTime;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; QueryPerformanceFrequency(&amp;m_nFreq); <span style="COLOR: green">// </span></span><span style="FONT-SIZE: 9pt; COLOR: green">获取时钟周期</span><br><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; QueryPerformanceCounter(&amp;m_nBeginTime); <span style="COLOR: green">// </span></span><span style="FONT-SIZE: 9pt; COLOR: green">获取时钟计数</span><br><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; Sleep(100);<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; QueryPerformanceCounter(&amp;nEndTime);<br>&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; (nEndTime.QuadPart-m_nBeginTime.QuadPart)*1000/m_nFreq.QuadPart &lt;&lt; endl;</span></div>
<div><strong>原理：</strong>CPU上也有一个计数器，以机器的<span>clock为单位，可以通过rdtsc读取，而不用中断，因此其精度与系统时间相当。</span></div>
<div><strong>精度：</strong>计算机获取硬件支持，精度比较高，可以通过它判断其他时间函数的精度范围。</div>
<div><strong>10</strong><strong>小结：</strong>以上提到常用的9种时间函数，由于他们的用处不同，所以他们的精度也不尽相同，所以如果简单的延时可以用sleep函数，稍微准确的延时可以使用clock函数，GetTickCount函数，更高级的实用 timeGetTime函数；简单的定时事件可以用Timer，准确地可以用timeSetEvent；或取一般系统时间可以通time，或者 CTime，或者COleDateTime，获取准确的时间可以用clock，或者GetTickCount函数，或者timeGetTime函数，而获取准确地系统时间要使用硬件支持的QueryPerformanceFrequency函数，QueryPerformanceCounter函数。</div>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/73148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-02-06 23:32 <a href="http://www.cppblog.com/beautykingdom/archive/2009/02/06/73148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>