第二次调用 NetworkStream BeginRead() 大规模资源争用
Second Call to NetworkStream BeginRead() Massive Resource Contention
我正在用 TcpClient
和 NetworkStream
对象编写一个 server/client 项目。我希望许多客户端连接到服务器,这些客户端存储在 List<>
个自定义 NetworkNode
对象中,每个对象都有一个 TcpClient
和一个 NetworkStream
用于与各自的客户。
服务器需要能够保持与客户端的连接,并在接收到消息时立即(快速)等待和操作消息。同步轮询对于这个应用程序来说是非常不可取的,我也不想那样编码。
目前服务器正在异步接受客户端并将它们添加到 List<>
,工作非常顺利。我已经使用控制台应用程序对此进行了测试,该应用程序最多可生成 100 个客户端,并在很短的时间内(< 1 秒)连接到环回地址处的服务器。
当客户端添加到 List<>
对象时,对象使用 GetStream()
方法 return 客户端的 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()
中使用的模式相同,所以我认为它的运行方式一定不同。
感谢您提供的所有建议,并在此先感谢您的帮助!
检查您的 _bytesRead
是否为 0,因为这意味着您的流已在远程端关闭。再次在这样的流上调用 BeginRead
将直接导致一次又一次地调用您的回调,读取字节数为 0。
我正在用 TcpClient
和 NetworkStream
对象编写一个 server/client 项目。我希望许多客户端连接到服务器,这些客户端存储在 List<>
个自定义 NetworkNode
对象中,每个对象都有一个 TcpClient
和一个 NetworkStream
用于与各自的客户。
服务器需要能够保持与客户端的连接,并在接收到消息时立即(快速)等待和操作消息。同步轮询对于这个应用程序来说是非常不可取的,我也不想那样编码。
目前服务器正在异步接受客户端并将它们添加到 List<>
,工作非常顺利。我已经使用控制台应用程序对此进行了测试,该应用程序最多可生成 100 个客户端,并在很短的时间内(< 1 秒)连接到环回地址处的服务器。
当客户端添加到 List<>
对象时,对象使用 GetStream()
方法 return 客户端的 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()
中使用的模式相同,所以我认为它的运行方式一定不同。
感谢您提供的所有建议,并在此先感谢您的帮助!
检查您的 _bytesRead
是否为 0,因为这意味着您的流已在远程端关闭。再次在这样的流上调用 BeginRead
将直接导致一次又一次地调用您的回调,读取字节数为 0。