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

时间:2015-08-17 03:38:01

标签: c# multithreading asynchronous tcpclient networkstream

我正在使用TcpClientNetworkStream个对象编写服务器/客户端项目。我希望许多客户端能够连接到服务器,这些客户端存储在List<>个自定义NetworkNode个对象中,每个对象都有一个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个额外的线程)。

但是 - 即使只有单个客户端已连接,如果我尝试在Stream.BeginRead()方法中对readCallback()进行第二次调用 (如上所述)我遇到大量争用问题。对于单个客户端,第二次调用后的CPU使用率从~0%跳到30%到60%之间,并且线程数可以从11跳到35。

所以它是一个线程或递归问题,我觉得我应该等待一些事情,但是我不能在这里发生什么事情。 。这与我在TcpListener.BeginAcceptTcpClient()中使用的模式相同,所以我认为它必须以不同的方式运作。

我感谢您提供的所有建议,并提前感谢您的帮助!

1 个答案:

答案 0 :(得分:3)

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