当前位置:首页> 正文

完成端口与线程池的问题?可以多个iocp完成端口共用吗

完成端口与线程池的问题?可以多个iocp完成端口共用吗

本文目录

  • 完成端口与线程池的问题
  • 可以多个iocp完成端口共用吗
  • iocp原理
  • 为什么完成端口是win
  • 完成端口的重叠I/O
  • 用完成端口写的服务器程序接收不到客户端的数据
  • io完成端口 和 epllo的区别
  • 完成端口,到底一个CPU 建几个线程

完成端口与线程池的问题


1、一般式cpu个数的2倍+2。
2、select可以传入参数进行组合,包括FD_READ、FD_WRITE等。
3、并不完全需要,大数据量频繁收发可采用独立线程的方式。
4、可以。
5、理论可以支持2048个线程,但是根据具体应用不会开很多。监听线程,和收发线程就够了,还有一些额外的针对不同逻辑的工作线程。

可以多个iocp完成端口共用吗


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

iocp原理


IOCP简介
提到IOCP,大家都非常熟悉,其基本的编程模式,我就不在这里展开了。在这里我主要是把IOCP中所提及的概念做一个基本性的总结。IOCP的基本架构图如下:

如图所示,在IOCP中,主要有以下的参与者:

--》完成端口:是一个FIFO队列,操作系统的IO子系统在IO操作完成后,会把相应的IO packet放入该队列。
--》等待者线程队列:通过调用GetQueuedCompletionStatus API,在完成端口上等待取下一个IO packet。
--》执行者线程组:已经从完成端口上获得IO packet,在占用CPU进行处理。
除了以上三种类型的参与者。我们还应该注意两个关联关系,即:
--》IO Handle与完成端口相关联:任何期望使用IOCP的方式来处理IO请求的,必须将相应的IO Handle与该完成端口相关联。需要指出的时,这里的IO Handle,可以是File的Handle,或者是Socket的Handle。
--》线程与完成端口相关联:任何调用GetQueuedCompletionStatus API的线程,都将与该完成端口相关联。在任何给定的时候,该线程只能与一个完成端口相关联,与最后一次调用的GetQueuedCompletionStatus为准。


为什么完成端口是win


1. 完成端口的实质
个人感觉完成端口就是一个“闹钟”,它可以被当做任何事情的提醒设备。比如说,(没有试过)ReadFile可以异步操作,可以将这个异步操作的完成这个事件的提醒交给完成端口来完成。所以,原文中所使用的完成端口,只是将这个“闹钟”用在了网络传输的提醒上。这也就说明了,尽管我们可以使用完成端口来完成各种提醒,但是主框架仍然是Socket那套流程。
为了更清晰地表达,请看下面的描述(从上往下)。这是作者的代码的API调用流程。其实我不太喜欢作者的第三部分的那些流程图,因为其中用了太多的自定义函数而掩盖了底层的API调用。现在我把他还原。
初始化库,WSAStart()
创建完成端口,CreateIoCompletionPort(-1,...);
创建工作线程,CreateThread()
创建listen socket,WSASocket
绑定listen socket 至完成端口,CreateIoCompletionPort(socket, …)
listen socket与本地端口绑定 ,bind()
开始监听,listen()
向listen socket投递accept请求,AcceptEx()
干其他事情或者等待结束命令
通信结束,通知Worker线程们退出,PostQueuedCompletionStatus()
至于Working 线程的做法和原先一样。
可以看到,尽管我们说完成端口的性能是最好的,然而它只是一个时间走得特别准的“闹钟”而已。 网络编程的基本框架还是原来的那套。
2. 关于Accept投递和WSARev投递
整偏博文里我看得最头痛的就是这个“投递”。它到底是什么?
刚才说了,这里只是把完成端口作为“提醒”机制。具体来说,就是listen之后,通常的调用是如下面所示的
While(1)
{
Socket = Accept();
_beginthreadex(…); // 传入socket
}
只不过,这样的调用就是阻塞型调用。而使用完成端口的话,就是将这个Accept(当然,源代码中使用更复杂的AcceptEx,其实差不多的)的调用(其实在内部,就相当于一个要求被accept的请求)委托给完成端口,然后主线程马上返回,并且让后者来提醒之前已经创建好的、在线程函数中调用了GetQueuedCompletionStatus函数的某个线程。当然,至于是哪个线程拿到了,那就是系统调度问题,这对我们来说是黑盒的。这就是那个AcceptEx投递的含义了。说得通俗点,所谓的Accept投递,就是告诉完成端口,“我——listen socket,向你提交一个请求,如果你有客户端的连结请求了,请立即告诉我”。所以可以说,AcceptEx投递就是listen socket的一个IO请求而已。
同样的道理,如果我们不是让完成端口去通知Accept完成,而是去让它通知Receive操作完成,那就是原文中的“投递WSARev操作”了。显然,这是两种操作。
3. 关于工作线程中的一些关键点
那么作者在“详细”那一节第六步中说的那个难点是怎么回事呢?我的理解是这样的。负责监听完成端口的线程(就是之前已经创建好的、在线程函数中调用了GetQueuedCompletionStatus函数的某个线程),它们是在GetQueuedCompletionStatus返回之前是不知道到底是那个操作被“闹钟”了,再加上之前说过的,具体是哪个线程被“闹钟”是我们所不知道的。所以说,你根本就无法判断某个线程会被用做干嘛。所以就需要一种机制来分别了。作者采用的方法是,自定义一些说明性的信息,然后在投递的时候附带送过来,这样在接受的时候就可以通过这些附加信息加以区别了。这就是那个PER_SOCKET_CONTEXT和PER_IO_CONTEXT的作用了。
具体来说,如果是为投递Accept而准备的闹钟,那么某个线程GetQueuedCompletionStatus返回之后,将会获得和Accept投递有关的Socket和此请求的附加信息。和accept投递有关的socket是哪个呢?自然是用于做listen的那个socket(GetQueuedCompletionStatus函数的第三个参数返回它的上下文——其实就是一个和它相匹配的补充性结构,那是在将socket和完成端口绑定时传入的第三个参数)。
注意到一个socket上面可以有很多个请求(类比一个客户端/服务器连结上可以发送多次数据,每一次发送数据都需要通过一个“发送数据请求”),从而在listen socket上面,我们之前投递了多个acceptEx请求,那么到底是哪个请求到来了呢?其实对于AcceptEx请求而言,哪一个并不那么重要,反正大家的工作都是创建一个客户端/服务器连结socket,然后进行通信。然而如果真的重要了,那么就需要这个值了(比如说不同连结需要不同操作之类)。具体来说,GetQueuedCompletionStatus
倒数第二个参数OVERLAPPED*,返回的就是和这个请求相关的信息。它其实就是我们在投递这个请求的时候所附加给完成端口的说明性材料。注意看,这是一个OVERLAPPED结构指针,应该和我们自定义的PER_IO_CONTEXT没有关系。实则不然,回顾CIOCPModel::_PostAccept里面AcceptEx调用的最后一个参数实际上是,&pAcceptIoContext-》m_Overlapped。也就是说这里传回来的是PER_IO_CONTEXT的第一个成员m_Overlapped的地址。通过位于_WorkThread函数中的风骚宏CONTAINING_RECORD
就能够得到这个PER_IO_CONTEXT的首地址啦。
另外,如果投递的是WSARev请求呢?这个附加信息怎么穿进去?一样的啦,就是通过WSARev函数的OVERLAPPED结构体参数。这个结构体的实际作用,就是在异步IO操作之后,提供给你一些信息的。我们可以通过继承这个结构(或者和这里的做法一致——采用风骚宏CONTAINING_RECORD)来拓展它,让它包含我们需要的信息。
接下去的事情就好办了,根据自定义结构PER_IO_CONTEXT的成员m_OpType了解一些信息,知道这个回应,到底是listen socket的accept请求的回应(ACCEPT_POSTED)还是真正的客户端/服务器连结socket的发送或者接收请求的回应(RECV_POSTED和SEND_POSTED)。
如果是对accept请求的回应,那么返回的socket context和io context都是这个accept请求的,所以作者强调在_DoAccept中是一定不能够动这两个结构的。然而,如果是普通连结的发送/接收请求的回应,那么当然就是另外一回事情了。

完成端口的重叠I/O


首先来看看重叠I/O(Overlapped I/O):
重叠I/O(Overlapped I/O)机制允许发起一个操作,然后在操作完成之后接受到信息。对于那种需要很长时间才能完成的操作来说,重叠IO机制尤其有用,因为发起重叠操作的线程在重叠请求发出后就可以自由的做别的事情了。
在WinNT和Win2000上,提供的真正的可扩展的I/O模型就是使用完成端口(Completion Port)的重叠I/O。


用完成端口写的服务器程序接收不到客户端的数据


楼上的是c++的吧 服务器端:using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Net;using System.Net.Sockets;using System.Threading;namespace test4_1 public void listbox(string str) //开始监听 private void Listen() catch (Exception e) } //接受请求回调函数,并在该回调函数中调用接收数据 public void acceptCallback(IAsyncResult ar) //接收数据回调函数 public void readCallback(IAsyncResult ar) catch(Exception ex) } private void send(string str) private void sendCallBack(IAsyncResult ar) private void buttonSend_Click(object sender, EventArgs e) }}客户端:using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Net;using System.Net.Sockets;using System.Threading;namespace test4_2 public void listbox(string str) //连接方法 public void Connect() catch (Exception e) } //连接方法的回调函数 private void ConnectCallback(IAsyncResult ar) catch (Exception e) } public void receivecallback(IAsyncResult ar) catch } //发送方法 private void Send(String data) //发送方法的回调函数 private void SendCallback(IAsyncResult ar) catch (Exception e) } private void buttonSend_Click(object sender, EventArgs e) }} 0

io完成端口 和 epllo的区别


完成端口 完成端口基本上公认为一种在windows服务平台上比较成熟和高效的IO方法,利用完成端口进行重叠I/O的技术在WindowsNT和WIndows2000上提供了真正的可扩展性。完成端口和Windows Socket2.0结合可以开发出支持大量连接的网络服务程序。

完成端口,到底一个CPU 建几个线程


接下来看看完成端口(Completion Ports )
其实可以把完成端口看成系统维护的一个队列,操作系统把重叠IO操作完成的事件通知放到该队列里,由于是暴露 “操作完成”的事件通知,所以命名为“完成端口”(Completion Ports)。一个socket被创建后,可以在任何时刻和一个完成端口联系起来。
一般来说,一个应用程序可以创建多个工作线程来处理完成端口上的通知事件。工作线程的数量依赖于程序的具体需要。但是在理想的情况下,应该对应一个CPU创建一个线程。因为在完成端口理想模型中,每个线程都可以从系统获得一个“原子”性的时间片,轮番运行并检查完成端口,线程的切换是额外的开销。在实际开发的时候,还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作,系统则将其挂起,让别的线程获得运行时间。因此,如果有这样的情况,可以多创建几个线程来尽量利用时间。
总之,开发一个可扩展的Winsock服务器并非十分困难的。主要是开始一个监听socket,接收连接,并且进行重叠发送和接收的IO操作。最大的挑战就是管理系统资源,限制重叠Io的数量,避免内存危机。遵循这几个原则,就能帮助你开发高性能,可扩展的服务程序。
socket的接收缓冲,因为接收事件仅仅在AcceptEx调用中发生。保证每个socket都有一个接收缓冲不会造成什么危害。一旦客户端/服务器在最初的一次请求(由AcceptEx完成)之后进行交互,发送更多的数据,那么取消接收缓冲更是一个很不好的做法。除非你能保证这些数据都是在每个连接的重叠IO接收里完成的 。

向左转|向右转


展开全文阅读

相关内容