2014-11-25 101 views
1

我是一个新的.NET开发人员,我正在阅读async/await。我需要处理一个框架,用于测试通过使用TCP远程访问服务器以及从这些服务器读取/写入数据来控制的设备。这将用于单元测试。不断从NetworkStream读取异步

没有应用层协议,服务器可能会根据外部事件发送数据。因此,我必须能够连续捕获来自服务器的任何数据并将其写入缓冲区,并可以从不同的上下文中读取。

我的想法去某处沿下面的代码片段的台词:

// ... 
private MemoryStream m_dataBuffer; 
private NetworkStream m_stream; 
// ... 

public async void Listen() 
{ 
    while (Connected) 
    { 
    try 
    { 
     int bytesReadable = m_dataBuffer.Capacity - (int)m_dataBuffer.Position; 

     // (...) resize m_dataBuffer if necessary (...) 

     m_stream.ReadTimeout = Timeout; 

     lock (m_dataBuffer) 
     { 
     int bytesRead = await m_stream.ReadAsync(m_dataBuffer.GetBuffer(), 
      (int)m_dataBuffer.Position, bytesReadable); 
     m_stream.Position += bytesRead; 
     } 
    } 
    catch (IOException ex) 
    { 
     // handle read timeout. 
    } 
    catch (Exception) 
    { 
     throw new TerminalException("ReadWhileConnectedAsync() exception"); 
    } 
    } 
} 

这似乎有以下缺点:

  1. 如果在调用和等待Listen功能,主叫挂机,甚至尽管调用者必须能够继续(因为只要连接打开,网络流应该被读取)。
  2. 如果声明它为async void而不等待它,则应用程序在任务中发生异常时会崩溃。
  3. 如果声明async Task而不是等待它,我假设发生同样的事情(加上我得到一个警告)?

以下问题随之而来:

  • 我能赶上在Listen抛出,如果我不期待它的异常?
  • 有没有更好的方法来使用async/await不断从网络流中读取?
  • 尝试使用async/await从网络流中连续读取或者是一个线程是一个更好的选择是否实际上理智?
+0

假设你正在使用.NET Framework 4.5,当在异步方法未处理的异常处理不应该赶上。从异步无效方法抛出异常又有什么意义(因为它不能被注意到)? – 2014-11-25 13:02:08

回答

1

async void应该至少是async Task与返回值扔掉。这使得该方法遵循标准的标准,并将责任推送给调用者,该调用者更有能力做出关于等待和错误处理的决定。

但是,您不必扔掉返回值。您可以将记录延续:

async Task Log(Task t) { 
try { await t; } 
catch ... 
} 

而且使用这样的:

Log(Listen()); 

扔掉由Log返回的任务(或者,等待它,如果你想在逻辑等)。

或者,只需在Listen中试一试即可。这似乎已经是这种情况了。

如果我不等待它,可以捕获Listen中抛出的异常吗?

您可以使用任何附加延续或等待同步的方式(后者不是您的策略)了解异常。

有没有更好的方法来使用async/await不断读取网络流?

不,这是它应该完成的方式。在任何时候都应该有一个读取未完成的IO。 (或在短时间内为零)。

尝试使用async/await连续读取网络流或者是线程是更好的选择是否实际上是否理智?

两者都会正常工作。 There is a trade-off to be made.同步代码可以更简单,更容易调试,甚至更少占用CPU。异步代码保存在线程堆栈内存和上下文切换。在UI应用程序await有显着的好处。

+1

关于外部日志记录和通过将输出附加到延续来处理异常的观点肯定帮助了我。 – moktor 2014-11-26 15:37:15

0

我会做这样的事情:

const int MaxBufferSize = ... ; 
Queue<byte> m_buffer = new Queue<byte>(MaxBufferSize); 
NetworkStream m_stream = ... ; 

... 

// this will create a thread that reads bytes from 
// the network stream and writes them into the buffer 
Task.Run(() => ReadNetworkStream()); 


private static void ReadNetworkStream() 
{ 
    while (true) 
    { 
     var next = m_stream.ReadByte(); 
     if (next < 0) break; // no more data 

     while (m_buffer.Count >= maxBufferSize) 
      m_buffer.Dequeue(); // drop front 

     m_buffer.Enqueue((byte)next); 
    } 
} 
+0

那么你会在线程池线程上运行一个固有的IO绑定操作? – 2014-11-25 14:35:44

+0

@DanielKelley根据我的理解,你说这是一个坏主意。这是因为他使用了阻塞“ReadByte()”,并且即使没有读取数据,上下文切换到“ReadNetworkStream()”任务也会发生。所以这个线程可能会在没有做任何事情的情况下烧毁CPU时间 – moktor 2014-11-26 15:41:12

+0

从他所说的话我明白,他不喜欢线程池中的线程永远被锁定。 @DanielKelley:谨慎发表描述/建设性意见? – 3dGrabber 2014-11-27 08:09:01