代码之家  ›  专栏  ›  技术社区  ›  khargoosh

第二次调用NetworkStream BeginRead()大规模资源竞争

  •  2
  • khargoosh  · 技术社区  · 9 年前

    我正在用编写服务器/客户端项目 TcpClient NetworkStream 物体。我希望许多客户端连接到服务器,这些客户端存储在 List<> 自定义的 NetworkNode 对象,每个对象都有 TCP客户端 和一个 网络流 用于与相应的客户端通信。

    服务器需要能够保持与客户端的连接,并在收到消息后立即(快速)等待和操作消息。同步轮询对于这个应用程序来说是非常不可取的,我也不想这样编码。

    当前,服务器正在异步接受客户端并将它们添加到 列表<> ,工作非常顺利。我已经用一个控制台应用程序测试了这一点,该应用程序生成了多达100个客户端,并在极短的时间内(<1秒)以环回地址连接到服务器。

    将客户端添加到 列表<> 对象使用 GetStream() 方法返回客户端的 网络流 对象我正在尝试使用 NetworkStream.BeginRead() 方法来实现来自每个TCP客户端的异步数据接收。对该方法的第一次调用如下:

    this.Stream.BeginRead(readBuffer, readBufferOffset, readBuffer.Length - readBufferOffset, 
        new AsyncCallback(nodeStreamReadCallback), this.Stream);
    

    因为控制台测试应用程序一连接到服务器就发送一些数据 readCallback(IAsyncResult) 方法几乎立即调用:

    private void readCallback(IAsyncResult ar)
    {
        NetworkStream _stream = (NetworkStream)ar.AsyncState;
    
        int _bytesRead = 0;
    
        _bytesRead = _stream.EndRead(ar);
    
        this.Stream.Write(readBuffer, readBufferOffset, _bytesRead);
    
        //increase buffer offset value
        readBufferOffset += _bytesRead;
    
        //TODO process the received data
        ...
    
        //wait for the next chunk of data
        this.Stream.BeginRead(readBuffer, readBufferOffset, readBuffer.Length - readBufferOffset, 
        new AsyncCallback(readCallback), this.Stream);
    }
    

    我正在执行第二次呼叫 Stream.BeginRead() 目的是等待下一个数据块到达或变得可用。

    当我对 Stream.BegginRead() 一切都很顺利。所有数据都被接收并发送回每个客户端,没有延迟,线程使用量极低(过程中平均增加2到3个线程)。

    然而,即使只有 单个客户端 已连接,如果我尝试 第二次呼叫 Stream.BegginRead() readCallback() 我遇到的方法 大量的 竞争问题。对于单个客户端,第二次调用后的CPU使用率从约0%跳到30%到60%之间,线程数可以从11跳到35。

    所以这是一个线程或递归问题,我觉得当我不在的时候,我应该等待一些东西,但我无法完全理解这里发生的事情。这与我在 TcpListener.BeginAcceptTcpClient() 所以我认为它的运作方式一定不同。

    我感谢您可能提供的任何和所有建议,并提前感谢您的帮助!

    1 回复  |  直到 9 年前
        1
  •  3
  •   abto    9 年前

    检查您的 _bytesRead 对于0,因为这意味着您的流已在远程端关闭。使命感 BeginRead 在这样的流上再次调用将直接导致一次又一次调用读取字节计数为0的回调。