我是一位非常新的.NET开发人员,我目前正在阅读async
/ await
。我需要研究用于测试设备的框架,这些设备通过使用TCP远程访问服务器以及从/向这些服务器读取/写入数据来控制。这将用于单元测试。
没有应用层协议,服务器可能会根据外部事件发送数据。因此,我必须能够连续捕获来自服务器的任何数据并将其写入缓冲区,缓冲区可以从不同的上下文中读取。
我的想法与以下代码段相似:
// ...
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");
}
}
}
这似乎有以下缺点:
Listen
功能,呼叫者将挂起,即使呼叫者必须能够继续(因为只要连接打开就应该读取网络流)。async void
而不等待它,则应用程序会在任务中发生异常时崩溃。 async Task
并且没有等待它,我会假设同样的情况发生(加上我收到警告)?随后出现以下问题:
Listen
中引发的异常吗?async
/ await
不断从网络流中读取数据? async
/ await
继续从网络流中读取,或者是一个更好的选择是不是真的很明智?答案 0 :(得分:1)
async void
至少应为async Task
并且丢弃返回值。这使得该方法符合理智的标准,并将责任推送给调用者,调用者可以更好地做出有关等待和错误处理的决策。
但你不必丢弃返回值。您可以附加日志记录延续:
async Task Log(Task t) {
try { await t; }
catch ...
}
并像这样使用它:
Log(Listen());
丢弃Log
返回的任务(或等待它,如果你想在逻辑上等待)。
或者,只需将Listen
中的所有内容包装在try-catch中即可。这似乎已经是这种情况了。
如果我不等待它,我可以捕获Listen中抛出的异常吗?
您可以使用任何附加延续或同步等待的方式查找异常(后者不是您的策略)。
是否有更好的方法可以使用async / await持续读取网络流?
不,这是它应该完成的方式。在任何给定时间应该有一个读取IO未完成。 (或者在短时间内为零。)
尝试使用async / await连续读取网络流是否真的是理智的,或者是一个更好的选择?
两者都能正常工作。 There is a trade-off to be made.同步代码可以更简单,更容易调试,甚至更少的CPU密集。异步代码保存在线程堆栈内存和上下文切换中。在UI应用中,await
具有显着优势。
答案 1 :(得分: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);
}
}