一尘不染

如何在C#中从同步方法调用异步方法?

c#

我有一个public async void Foo()要从同步方法调用的方法。到目前为止,我从MSDN文档中看到的所有内容都是通过异步方法调用异步方法,但是我的整个程序不是使用异步方法构建的。

这有可能吗?

这是从异步方法调用这些方法的一个示例:http : //msdn.microsoft.com/zh-
cn/library/hh300224(v=vs.110).aspx

现在,我正在研究从同步方法调用这些异步方法。


阅读 1335

收藏
2020-05-19

共1个答案

一尘不染

异步编程确实在代码库中“增长”。它已经被比作僵尸病毒。最好的解决方案是允许它增长,但是有时这是不可能的。

我在Nito.AsyncEx库中编写了一些类型,用于处理部分异步的代码库。但是,没有一种解决方案可以在每种情况下都适用。

解决方案A

如果您有一个简单的异步方法,不需要同步回其上下文,则可以使用Task.WaitAndUnwrapException

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

希望使用Task.WaitTask.Result因为包装在异常AggregateException

仅当MyAsyncMethod不同步回其上下文时,此解决方案才适用。换句话说,每个awaitin
MyAsyncMethod都应以结尾ConfigureAwait(false)。这意味着它无法更新任何UI元素或访问ASP.NET请求上下文。

解决方案B

如果MyAsyncMethod确实需要同步回其上下文,那么您可能可以AsyncContext.RunTask用来提供嵌套的上下文:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

  • 2014年4月14日更新:在该库的最新版本中,API如下:

    var result = AsyncContext.Run(MyAsyncMethod);


Task.Result在此示例中可以使用,因为RunTask它将传播Task异常)。

之所以需要AsyncContext.RunTask而不是Task.WaitAndUnwrapException因为WinForms / WPF /
SL / ASP.NET上发生相当微妙的死锁:

  1. 同步方法调用异步方法,获得Task
  2. 同步方法对进行阻塞等待Task
  3. async方法await不使用ConfigureAwait
  4. Task这种情况无法完成,因为它只有在完成async方法完成; 该async方法无法完成,因为它正在尝试将其继续安排到SynchronizationContext,并且WinForms / WPF / SL / ASP.NET将不允许继续运行,因为同步方法已在该上下文中运行。

这就是为什么ConfigureAwait(false)在每种async方法中尽可能多使用一个好主意的原因之一。

解决方案C

AsyncContext.RunTask并非在所有情况下都有效。例如,如果async方法等待需要完成UI事件的操作,那么即使使用嵌套上下文,您也将死锁。在这种情况下,您可以async在线程池上启动该方法:

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

但是,此解决方案需要一个MyAsyncMethod可以在线程池上下文中工作的。因此,它无法更新UI元素或访问ASP.NET请求上下文。在这种情况下,您也可以添加ConfigureAwait(false)await语句,并使用解决方案A。

更新,2019-05-01: MSDN文章在此处提供了当前的“最差实践” 。

2020-05-19