如何正确阻止异步代码? [英] How to correctly block on async code?

查看:132
本文介绍了如何正确阻止异步代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have tons of code written in following manner:

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

在这里,我们有一些必须访问异步api的同步代码,因此它将阻塞,直到结果准备就绪为止.我们无法通过方法更改签名并在此处添加async.因此,无论如何我们都在同步等待,所以我们在这里需要ConfigureAwait(false)吗?我很确定我们不会,但是我有点删除它,因为它可能涵盖了一些用例(或者为什么我几乎在任何地方都看到它?这只是一个货真价实的行为?),并且删除此调用可能导致一些不安全的结果.

Here we have some sync code that have to access to async api, so it blocks until results are ready. We can't method change signature and add async here. So, we are waiting synchronously anyway, so do we need ConfigureAwait(false) here? I'm pretty sure that we don't, but I'm a bit affraid of removing it because it's probably covers some use cases (or why am I seeing it virtually everywhere? It's just a cargo cult?) and removing this call may lead to some unsafe results.

那么这有道理吗?

推荐答案

如何正确阻止异步代码?

How to correctly block on async code?

您没有正确阻止异步代码.阻止是错误的.询问做错事情的正确方法是一门简单的菜.

You do not correctly block on async code. Blocking is wrong. Asking what the right way is to do the wrong thing is a non-starter.

由于以下情况,阻止异步代码是错误的:

Blocking on async code is wrong because of the following scenario:

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

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

And now you can figure out what goes horribly wrong when you attempt to fetch the result synchronously of the first async operation. It blocks until its child async operation is finished, which will never happen, because now we've blocked the thread that is going to service the request in the future!

您的选择是:

  1. 使整个调用堆栈正确异步,并await结果.
  2. 请勿使用此API.从头开始编写一个您知道不会死锁的等效同步API,并正确调用它.
  3. 编写不正确的程序,有时会死锁.
  1. Make your entire call stack correctly asynchronous and await the result.
  2. Don't use this API. Write an equivalent synchronous API that you know does not deadlock, from scratch, and call it correctly.
  3. Write an incorrect program which sometimes deadlocks unpredictably.

有两种方法可以编写正确的程序;在异步函数上编写同步包装程序是危险和错误的.

There are two ways to write a correct program; writing a synchronous wrapper over an asynchronous function is dangerous and wrong.

现在,您可能会问,ConfigureAwait不能通过删除我们在当前上下文中恢复的要求来解决问题吗? 这不是我们担心的恢复点.如果您要依靠ConfigureAwait来避免死锁,那么堆栈中的每个异步操作都必须使用它,而且我们不知道底层异步操作是否要导致死锁做到了!

Now, you might ask, didn't the ConfigureAwait solve the problem by removing the requirement that we resume on the current context? That's not the resumption point that we're worried about. If you're going to rely on ConfigureAwait to save you from deadlock then every asynchronous operation in the stack has to use it, and we don't know if the underlying asynchronous operation that is about to cause the deadlock did that!

如果以上内容对您而言还不是很清楚,请阅读Stephen的文章,这是为什么不好的做法,以及常见的变通办法只是危险的骇客.

If the above is not entirely clear to you, read Stephen's article on why this is a bad practice, and why common workarounds are just dangerous hacks.

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

及其更新的文章在此处提供了更多的技巧和解决方法:

and his updated article giving more hacks and workarounds here:

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

但是再说一遍:正确要做的是重新设计程序以包含异步性并在整个过程中使用await.不要尝试解决它.​​

But again: the right thing to do is to redesign your program to embrace asynchrony and use await throughout. Don't try to work around it.

因为此方法具有〜20个方法的堆栈跟踪,所以其中一些正在实现某些接口.要将其更改为异步,需要在〜50个文件中进行更改声明,然后我们将完全同步的接口转换为混合接口.

becuase this method has stacktrace of ~20 methods, some of them are implementing some interfaces. Changing it to be async require change declarations in ~50 files, and we convert fully sync interfaces to mixed ones.

那就忙吧!听起来很简单.

Get busy then! This sounds pretty easy.

这篇关于如何正确阻止异步代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆