长期阻塞方法。阻塞,睡眠,开始/结束和异步之间的区别

时间:2013-02-25 03:33:39

标签: c# blocking networkstream thread-sleep

这个问题与设计或模式以及使用方法无关。这个问题的核心是关于线程和阻塞的情况。

此示例适用于任何旨在连续执行相同操作的阻止方法。在这种情况下,它是网络流上的阻塞读取或写入。幕后有关于方法之间的线程和性能的明显差异吗?

我的假设是下面的每个方法都创建一个线程或使用一个池化线程。然后阻塞该线程直到有数据要读取。话虽如此,在这种情况下,方法之间的线程,性能和可伸缩性是否存在明显的差异?

目前我正在创建一个服务器应用程序。此应用程序将有1000个客户端创建TCP连接。这些连接将保持打开状态,经常发送和接收少量数据。我希望使用模型A,因为它是最容易实现和最易维护的。无论选择哪种模式,我最终都会得到1000条线程吗?

请注意,这些方法只是为了了解结构,而不是在没有正确的流读取,超时和异常处理的情况下使用的方法。

方法A:阻止

Task.Factory.StartNew(ReadMessage,TaskCreationOptions.LongRunning);
private void ReadMessage()
{
   while(true)
   {
      TcpClient.Read();
   }
}

方法B:睡觉

Task.Factory.StartNew(ReadMessage,TaskCreationOptions.LongRunning);
private void ReadMessage()
{
    while(true)
    {
        if(TcpClient.DataAvailable)
            TcpClient.Read();
        else
            Thread.Sleep(1);
    }
}

方法C:递归开始/结束

private void ReadMessage()
{
      stream.BeginRead(readCallBack)
}
private void readCallBack()
{
      stream.EndRead();
      stream.BeginRead(readCallBack)
}

方法D:来自BCL socket.ReceiveAsync()的异步

private void readCallBack()
{
    while(true)
    {
        await socket.ReceiveAsync(eventArgs);
    }
}

方法E:使用阻塞的异步方法读取(使用方法D来调用,但是使用自定义方法而不是使用BCL中内置的套接字)

private async Task<byte[]> ReceiveAsync()
{
   return await Task.Factory.StartNew(() => TcpClient.Read());
}

1 个答案:

答案 0 :(得分:1)

  

我的假设是下面的每个方法都创建一个线程或使用一个池化线程。然后阻塞该线程直到有数据要读取。

完全没有。前两个示例阻止了线程,但后两个示例是异步的。

异步方法的工作原理是将工作排队到操作系统,然后等待回调,在本例中是在I / O完成端口上。因此,当读取处于挂起状态时,没有使用线程。

由于异步方法不使用尽可能多的线程,因此它们可以更好地扩展。

您的上一个示例(async)实际上就像您的第一个示例一样简单,除非您使用Rx或TPL数据流,否则这将是我推荐的方法。在进行套接字通信时,当您考虑错误处理(例如检测到丢弃的连接)时,异步通信显然是可行的方法。