大家都知道,完成端口的并发模型在多个工作者线程同时使用一个完成端口的时候,会有烦人的并发资源访问和同步的问题.
如果是只用一个工作者线程,那么这样的问题就可以大大的避免掉;但是如果只有一个线程,那么完成端口的优势会被掩盖掉.
    所以,我的想法是,为每个工作者线程创建一个完成端口,这样一对一的访问操作,就可以避免掉资源并发访问的冲突了.
不知道这样的方法是否可行?一个程序里开多个完成端口会不会影响效率?请大家给个意见吧

解决方案 »

  1.   

    方法肯定是可以的,但是这样做才真的是把完成端口的优势掩盖住
    你这样的思想何必用IOCP呢?用其他的模型会好更多
      

  2.   

    理论上讲是可行的,即在创建与客户端连接时绑定到不同的完成端口内核对象上。但如果每一个连接都绑定一个新的完成端口上也是不经济的,因为那样就意味着会为每一个连接创建一个工作线程,想想如果有成千上万个连接的时候,线程切换都会是很大的负担,反而不能体现IOCP的性能优势了。
    个人觉得如果想用这种方式解决线程的同步,是不太可取的。还是管理好线程比创建完成更多的完成端口有效。
      

  3.   

    其他的模型没有办法是用iocp的设计原理啊,比如perhandle数据,这个是很好用的啊,可以减少很多的查表操作,直接快速的定位到客户端连接对象上;还有网络数据收发上的优化,等等。只是唯一的区别就是我只有一个线程,而通常的做法是多个线程公用一个完成端口。
    而且我认为这样还不会出现收发乱序的问题,在多线程情况下,要处理收发乱序就要附加而外的同步机制来实现,这是会增加消耗的。
      

  4.   

    呵呵, 楼主还是稍微理一下思路
    IOCP中,我们所说的工作线程,是服务于某个已经和某个句柄对象绑定的完成端口。为这个完成端口的IO请求提供服务。楼主所说的每个线程关联一个完成端口,这里的线程应该不是指刚才说的工作线程。楼主的意思是用来监听socket的线程关联一个完成端口,用来进行数据IO的socket,在另外一个完成端口排队。一个端口用来负责接收连接,另外一个端口用来IO。想法是没错的,不过却不是IOCP模型了IOCP模型最为关键的前提,就是完成端口和IO对象的句柄关联(文件句柄,或者socket句柄),而不是与承担某个功能的线程关联,线程的句柄是内核对象,但不是IO对象。工作线程为完成端口上的IO完成消息(意思是系统负责这些IO的完成,然后把完成的消息和数据放入某个完成端口的消息队列中)承担服务。msdn上明确指出,一个工作线程只能为最多不超过一个完成端口提供服务。也就是说,你可以让工作线程为不同的完成端口去服务,但是前提是必须首先释放与前一个完成端口的联系。相当于预先有了完成端口池,你仍然必须为这些完成端口实现调度管理,这无疑增加了模型的复杂性。
    个人认为,与其如此,不如多路端口多进程监听,进行负载平衡调度,用其他IO空闲的监听端口实现多路IO。服务器端,不一定要局限于单个进程,多个进程在一些场合也很实用,而且适合分布式扩展。逻辑上的分布式处理,能够提高足够的并发,当然,极限IO的吞吐,是另外一回事,并不应该完全在负责接收连接的端口和进程上去实现。负责监听,并接受连接的端口的责任应该是为接下来可能的极限IO请求,分配一个空闲的通道。
      

  5.   

    vieri_ch说的还是有差别.
    光用语言描述,可能不好说明白,大家看的也糊涂。
    又不能画图,就用代码简要描述一下我的想法吧。//三个完成端口
    HANDLE hCompletionPort_ListenThread;
    HANDLE hCompletionPort_WorkThread_A;
    HANDLE hCompletionPort_WorkThread_B;SOCKET sockListen;//监听线程
    DWORD WINAPI ListenThread(LPVOID lpParam)
    {
      //第一步、创建三个完成端口。。
      //第二步、创建侦听套接字sockListen,并开始侦听
      //第三步、将监听套节字关联到完成端口
    ::CreateIoCompletionPort((HANDLE)sockListen, hCompletionPort_ListenThread, (DWORD)0, 0);

    //接下来就投递接受连接的请求到完成端口
      
      //进入循环,QCPS
      while ( TRUE )
      {
         BOOL bOK = ::GetQueuedCompletionStatus(hCompletionPort_ListenThread,...)
         //如果返回成功,有客户段连接上来,假设套接字句柄是sockClient,
         //就把这个套接字和hCompletionPort_WorkThread_A或者hCompletionPort_WorkThread_B关联起来。
         //和哪个完成端口关联,可以简单的使用轮询的策略。
         ::CreateIoCompletionPort( (HANDLE)sockClient, hCompletionPort_WorkThread_A,...); 
         
         //再投递接受连接的请求到hCompletionPort_ListenThread
      }
    }//工作者线程A
    DWORD WINAPI WorkThread_A(LPVOID lpParam)
    {
      while ( TRUE )
      {
         BOOL bOK = ::GetQueuedCompletionStatus(hCompletionPort_WorkThread_A,...)
         ...
      }
    }//工作者线程B
    DWORD WINAPI WorkThread_B(LPVOID lpParam)
    {
      while ( TRUE )
      {
         BOOL bOK = ::GetQueuedCompletionStatus(hCompletionPort_WorkThread_B,...) 
         ...
      }
    }
      

  6.   

    这样也是可以。有几个疑问假设,对于每个accept完成的socketclient
    socketclient1.关联到completionportA,并且在A线程中处理完成包。
    socketclient2.关联到completionportB,并且在B线程中处理完成包
    socketclient3,此时没有可用的端口。如何处理?
    什么情况下。socketclient1或者2,与各自的compeletionport解除关系?
    如果是长连接协议,1或者2,没有断开连接,是否应该释放与A或者B端口的关联?
    如果释放了关联(不是断开),1或者2出现了新的io请求,由哪一个完成端口负责?
    如果不释放关联(长连接),3何时才可以关联到可用的完成端口?假设你不止2个完成端口,那么完成端口的数量定义的策略是什么。
    一个完成端口对应一个socketclient?传统的。完成端口可以为多个连接服务,那是因为完成包中包含了客户端套接字的信息,并非是完成端口和这个客户端套接字句柄绑定。完成端口关联的仅仅是那个监听套接字句柄。更改关联,必须先释放。
      

  7.   

    回复vieri_ch:
    socketclient3,此时没有可用的端口。如何处理? 答:可以从completionportA或者completionportB中选择一个关联,这种选择可以根据一定的负载平衡选择,简单的就是从completionportA和completionportB中轮流取一个关联.一个完成端口可以和多个socket关联的啊,你的意思好像是说为了要让socketclient3和completionportA关联必须要先把socketclient1和completionportA解除关联.这怎么可能啊,不需要这样做的啊.你的说法好像不太准确:"完成端口可以为多个连接服务,那是因为完成包中包含了客户端套接字的信息,并非是完成端口和这个客户端套接字句柄绑定。"
    我觉得应该是:完成端口能为多个连接服务,是因为我把套接字和完成端口进行了绑定,所有和这个套接字相关的异步io请求都会通过这个完成端口进行通知.每个完成端口可以何多个套接字相关联的.2.完成端口数量的定义策略,很简单,就是CPU个数.
    让每个CPU运行一个线程.
      

  8.   

    你的说法好像不太准确:"完成端口可以为多个连接服务,那是因为完成包中包含了客户端套接字的信息,并非是完成端口和这个客户端套接字句柄绑定。" 
    我觉得应该是:完成端口能为多个连接服务,是因为我把套接字和完成端口进行了绑定,所有和这个套接字相关的异步io请求都会通过这个完成端口进行通知.每个完成端口可以何多个套接字相关联的. RE: 每个完成端口可以何多个套接字相关联的? 我认为这句话是不准确的,因为完成通知中,包含了一个socket信息,这个socket也就是accept返回的某个客户端对应的套接字,但仅仅是包含在完成通知中,并不是和这个完成端口关联。一个完成端口上可以有成千上万个完成通知,每个完成通知包里的socket信息可能对应于很多个不同客户端socket。我想,你说的跟多个套接字关联,是指这个吗?
      

  9.   

    To:vieri_ch
    你客气了,其实是我没有说清楚。那么现在我的意思可能大家都明白了吧?
    我的想法是,这样做的好处是:
    1、减少同步负担。由于对每个完成端口而言只有一个线程在为其服务,那么对于每个连接来说,就避免了多个线程的同步问题了。(当然,如果考虑到上层的应用是多线程的话,还是要考虑同步的,但是,毕竟底层的同步问题不存在了。)
    2、可以不必考虑接收到乱序问题。因为同一个链接的接收完成通知都是在同一个线程里返回,由于iocp保证的出完成队列的顺序,从而就不会有接收乱序的问题。这样有可以使设计简单许多。
    3、不必考虑发送顺序的问题。还是因为只有一个线程的原因。
    4、不会由于一个链路上的请求处理阻塞住线程,而出现所有的链路都无法处理的情况。因为我有多个完成端口同时在处理。一个完成端口上的某个链接的问题,不会导致其他完成端口上的io无法及时处理。以上是我的一点想法,希望大家提出意见,我想的是不是对的呢?
      

  10.   

    辛苦楼主了  可以试试 socket 与 完成端口的多次关联
      

  11.   

    CPU只有一个、网卡只有一个 
    你弄多个完成端口有P用
      

  12.   

    17楼的请注意你的言行举止。按照你的逻辑:我请问你,你会开几个ie?
    CPU只有一个、网卡只有一个?不要不懂装懂。开多个就是为了解决问题的。
      

  13.   

    如果是单纯的IO,那跟直接使用Overlapped当中的Completion Routine模式差别不太大了。
      

  14.   

    楼主的逻辑,其实就是让特定的操作(包括IO)排到特定的线程队列当中,仅仅只是使用完成端口作为队列管理模型。从理论上来讲这是可行的,但是并不一定能够完美地解决线程间的冲突问题。
    1. 之所以使用完成端口,就是要能够极大地利用CPU资源处理更多地事务。从而把所有的事务排到一个队列当中,由系统选择最后一个空闲的线程资源来执行该事务。
    2. 当使用多个完成端口之后,高密集度的事务排定计划被取代,相对来讲就会存在极大的随机性打个比方,现在有16个客户,同一时间有4个客户并发请求,服务器配有4个线程来工作:
    1. 在第一种模式当中,这4个客户请求会在同一时间由4个线程并行处理;
    2. 在第二种模式当中,这4个客户请求有可能被4个线程并行处理(最优的情况),但是也有可能被排一个线程当中串行处理(最差的情况)。其实两者都各有优劣势,所以在选择的时候,最好还是考虑实际情况,比如象FTP的数据传输连接,这种密集性业务处理,后者明显优于前者。对于前者存在的冲突性,也可以考虑其他解决方案
    1. 尽可能做资源本地化复用;
    2. 对部分资源进行线程化管理,例如线程局部存储;
    3. 减小并发访问控制粒度;
    ...
      

  15.   

    楼主不必太在意线程间同步的问题来影响IOCP原来设计的优势!
      

  16.   

    楼主的设计模式有问题,可能 是对完成端口的原理没有做太多了解。
    一般最简单的设计模式如下:
    1:.创建listensocket
    2:然后创建完成端口
    3:在创建accept线程绑定到完成端口上(一般5个就够了)// 可以根据用户连接频率自己调节
    4:再创建n个(一般五个就够了,可以根据用户连接频率自己调节)等待完成端口的通知。
    5:创建业务处理线程为了提高服务器的高并发性能
    第四步一般制作等待操作,等待到远程请求后,先复制用户请求,然后post到3让它再创建一个acceptsocket
    绑定到完成端口上,之后把复制下来的用户请求post给5,让5去处理用户请求,之后继续等待完成端口通知。这样就能做到,用户发送请求过来就能立马得到响应,无需死等。按照你的设计模式,一个客户端请求过来就绑定一个自己的完成端口,个人认为你还没有理解完成端口,以及为什么要使用完成端口。你这种设计模式服务器上最大客户端的连接数是要受限制的。可以参考ACE的有关代码。以上只是我个人的观点,可能没有弄清楚楼主想问的问题
      

  17.   

    (1) 单完成端口单线程
    (2) 单完成端口多线程
    (3) 多完成端口多线程
    (4) 单完成端口多进程
    在单CPU时,三种性能没什么区别。多CPU时(1)、(4)性能较差,而(2)、(3)性能较好,两者区别不大,(3)性能稍高。
    这么多人有凝问,为什么不做个性能测试呢!
    个人觉得,如果处理时间相对较长,用(2)就可以了,因为相对简单;如果是转发的服务器,处理时间很短,可用(3)