如何正确阻止异步代码?

时间:2017-07-11 12:23:49

标签: c# .net async-await configureawait

我有以下方式编写的大量代码:

public string SomeSyncOperation(int someArg)
{
   // sync code 
   SomeAsyncOperation(someArg, someOtherArg).ConfigureAwait(false).GetAwaiter().GetResult()
   // sync code
};

这里我们有一些必须访问异步api的同步代码,因此它会阻塞,直到结果准备就绪。我们无法通过方法更改签名并在此处添加async。所以,我们无论如何都在等待,所以我们在这里需要ConfigureAwait(false)吗?我很确定我们没有,但我有点害怕删除它,因为它可能涵盖了一些用例(或者为什么我几乎到处都看到它?它'&#39 ; s只是一个货物狂热?)并删除这个电话可能会导致一些不安全的结果。

所以它有意义吗?

1 个答案:

答案 0 :(得分:13)

  

如何正确阻止异步代码?

您没有正确阻止异步代码。阻止是错误的。询问做错事的正确方法是非首发。

由于以下情况,阻止异步代码出错:

  • 我手头有一个代表异步操作的对象。
  • 异步操作本身异步等待第二次异步操作完成。
  • 当消息循环执行与此线程的消息队列中当前消息相关联的代码时,将调度此线程的第二个异步操作。

现在,当您尝试同步获取第一个异步操作的结果时,您可以找出可怕的错误。它会阻塞,直到它的子异步操作完成,这将永远不会发生,因为现在我们已经阻止了将来为该请求提供服务的线程!

您的选择是:

  1. 使整个调用堆栈正确异步,await结果。
  2. 请勿使用此API。编写一个等效的同步API,你知道它不会从头开始死锁,并正确调用它。
  3. 写一个不正确的程序,有时会无法预测死锁。
  4. 有两种方法可以编写正确的程序;在异步函数上编写同步包装是危险和错误的。

    现在,您可能会问,ConfigureAwait是否通过删除我们在当前上下文中恢复的要求来解决问题? 这不是我们担心的恢复点。如果您依靠ConfigureAwait来保护您免于死锁,那么堆栈中的每个异步操作都必须使用它,而我们不知道底层的异步操作是否是即将造成僵局的确如此!

    如果上述内容对您来说并不完全清楚,请阅读Stephen关于为什么这是一个不好的做法的文章,以及为什么常见的解决方法只是危险的黑客攻击。

    https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

    他的更新文章提供了更多的黑客攻击和解决方法:

    https://msdn.microsoft.com/en-us/magazine/mt238404.aspx?f=255&MSPPError=-2147217396

    但是再一次:正确的要做的是重新设计你的程序以包含异步并在整个过程中使用await。不要试图解决它。

      

    因为这个方法有~20个方法的堆栈跟踪,其中一些正在实现一些接口。将其更改为异步需要在~50个文件中进行更改声明,并且我们将完全同步接口转换为混合接口。

    然后忙!这听起来很容易。