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

本文关键字:大规模 资源 争用 调用 NetworkStream BeginRead 第二次 | 更新日期: 2023-09-27 18:08:09

我正在编码一个服务器/客户端项目与TcpClientNetworkStream对象。我希望许多客户端连接到服务器,它存储在自定义NetworkNode对象的List<>中,每个对象都有一个TcpClient和一个NetworkStream,用于与各自的客户端通信。

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

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

当客户端被添加到List<>时,对象使用GetStream()方法返回客户端的NetworkStream对象。我试图使用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.BeginRead()的第二个调用时,一切都运行得非常顺利。所有数据都被接收并发送回每个客户端,没有延迟,并且线程使用极低(在此过程中平均2到3个额外线程)。

然而,即使只有一个单个客户端已经连接,如果我试图在readCallback()方法内对Stream.BeginRead()进行第二次调用(如上所述),我会遇到大量争用问题。对于单个客户机,第二次调用后的CPU使用率从~ 0%跳到30%到60%之间,线程数可以从11个跳到多达35个。

所以这是一个线程或递归问题,我觉得我应该等待一些东西,当我没有,但是我不能完全理解这里发生了什么。这是我与TcpListener.BeginAcceptTcpClient()使用的相同模式,所以我认为它必须以不同的方式操作。

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

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

检查您的_bytesRead是否为0,因为这意味着您的流在远程端已关闭。在这样的流上再次调用BeginRead将导致直接调用你的回调函数,并且一次又一次地读取字节数为0。