如何更新我的API以使用async关键字而不使用等待所有调用者

c0D*_*g1c 5 .net c# async-await .net-4.5

我正在尝试将一些TcpClient相关代码移植到.net 4.5,使用StreamSocketDataReader替代.

我有一个名为的函数ReadLine(),它在任何地方都使用.通过DataReaderLoadAsync()此代码的body()中使用,我的方法被强制用async关键字标记.

连锁反应如下:现在我有数百个地方需要添加async到调用方法并应用等待基础async方法调用.

这引出了我的下一个问题...是否有一种简单的方法来包装,ReadLine()以便调用方法不知道它是一个异步方法,所以我不必更改我的其余代码?

另外......我经常在一个循环中使用这个方法调用,来自多个地方.如果现在标记了这些方法async,我恐怕我可能会在不应该的时候从数据流中读取数据,这将导致各种恶梦.这是一个问题还是我想得太远了?

Ste*_*ary 8

另外......我经常在一个循环中使用这个方法调用,来自多个地方.如果这些方法现在被标记为异步,我恐怕我不应该在流中读取数据,这将导致各种恶梦.这是一个问题还是我想得太远了?

如果你总是await在调用*Async方法时使用,那么你的async方法就像同步方法一样(除了它们不会阻塞).因此,await在循环中使用将像您期望的那样工作.


async确实通过代码库"增长".我通常认为这与关于"乌龟一直向下"的旧故事类似; 其他人称之为"僵尸病毒".

我在博客上详细描述了死锁情况.正如我所说,最好的选择是允许async增长.

如果必须为异步方法创建同步包装器,请参阅Stephen Toub的建议.你可以使用Task.Result,但你需要做两件事:

  • ConfigureAwait(false)到处使用.这将回避僵局局面.
  • 请注意,它Result具有不同的错误处理语义.

对于您的特定示例,这样的事情就足够了:

private async Task<string> ReadLineAsync()
{
  ... // *Every* await in this method and every method it calls
      // must make use of ConfigureAwait(false).
}

public string ReadLine()
{
  try
  {
    return ReadLineAsync().Result;
  }
  catch (AggregateException ex)
  {
    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
    throw;
  }
}
Run Code Online (Sandbox Code Playgroud)

在选择混合同步/异步代码库之前,请仔细考虑复杂性.它并不像第一次出现那么容易.

PS一般来说,无论如何,TCP/IP代码都应该是异步的.在套接字上进行连续异步读取通常是个好主意.