C# 如何更新API以使用async关键字,而不为所有调用者使用Wait

C# 如何更新API以使用async关键字,而不为所有调用者使用Wait,c#,.net,.net-4.5,async-await,C#,.net,.net 4.5,Async Await,我试图使用StreamSocket和DataReader将一些TcpClient依赖代码移植到.NET4.5 我有一个名为ReadLine()的函数,在任何地方都可以使用。通过在此代码的正文(LoadAsync())中使用DataReader,我的方法被强制标记为async关键字 连锁反应如下: 现在我有数百个地方需要向调用方法添加async,并将wait应用于底层async方法调用 这就引出了我的下一个问题。。。 有没有一种简单的方法可以包装ReadLine(),这样调用方法就不会知道它是一个

我试图使用
StreamSocket
DataReader
将一些
TcpClient
依赖代码移植到.NET4.5

我有一个名为
ReadLine()
的函数,在任何地方都可以使用。通过在此代码的正文(
LoadAsync()
)中使用
DataReader
,我的方法被强制标记为
async
关键字

连锁反应如下: 现在我有数百个地方需要向调用方法添加
async
,并将wait应用于底层
async
方法调用

这就引出了我的下一个问题。。。 有没有一种简单的方法可以包装
ReadLine()
,这样调用方法就不会知道它是一个异步方法,这样我就不必更改其余的代码了

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

制作一个ReadLineSync函数和一个ReadLine函数。
在ReadLine函数中,可以如下方式调用ReadLineSync:

var readLineTask = ReadLineAsync(); 
readLineTask.Wait() 

将它标记为异步并不意味着无论何时调用它都必须等待它。之所以将其标记为异步,是因为在方法中使用了wait

这意味着您不必仅仅因为在那里调用
ReadLine()
就将其他方法标记为异步

要让readline方法等待,它必须返回
任务
任务

还有。。。我经常在多个地方的循环中使用此方法调用。如果这些方法现在标记为异步,我担心我可能会在不应该的时候从流中读取数据,这会引起各种恶梦。这是个问题还是我想得太远了

如果在调用
*Async
方法时始终使用
wait
,则
Async
方法的行为将与同步方法一样(除非它们不会阻塞)。因此,在循环中使用
wait
将像您预期的那样工作


async
确实通过代码库“增长”。我通常认为这类似于关于“海龟一路下来”的老故事;其他人称之为“僵尸病毒”

我在我的博客上详细描述了。正如我在这里指出的,最好的选择是允许
async
增长

如果您必须创建一个。您可以使用
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;
  }
}
private异步任务ReadLineAsync()
{
…//*Every*在此方法及其调用的每个方法中等待
//必须使用ConfigureWait(false)。
}
公共字符串读取行()
{
尝试
{
返回ReadLineAsync()。结果;
}
捕获(聚合异常)
{
ExceptionDispatchInfo.Capture(例如InnerException.Throw();
投掷;
}
}

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


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

您真的需要使ReadLine异步吗?我不希望它是异步的,但是因为我在这个方法中使用了wait,编译器强迫我用async来标记它,这会引发连锁反应。将它标记为async并不意味着每次调用它时都要等待它。您之所以将其标记为异步,是因为您在方法中使用了wait。我应该提到ReadLine()是一个返回任务的函数。@c0D3l0g1c Ok,但它是否实现了所有其他可等待的要求?i、 e.GetAwaiter/BeginAwait/EndAwait?如果确实如此,那么您必须修改您的代码,正如您所描述的:)这将使编译器满意,但它不会等待ReadLine完成。你现在有了一个比赛条件。请注意,这至少会在WinForms和ASP.NET中导致确定性死锁。您的建议非常有意义。在ReadLine方法中,我调用Result.Perfect,而不是等待ReadLineAsyn。任务完成后,调用任务结果。回答得很好!优秀的博客文章!