为什么总是建议在每条带有await的行上编写ConfigureAwait(false),我真的需要吗?

维克多·阿萨诺夫(Viktor Arsanov)

问题不在于ConfigureAwait的功能。而是为什么为什么到处我都会看到类似的东西

通常,是的。除非该方法需要其上下文,否则应将ConfigureAwait(false)用于每次等待。

即他们提议我应该写

await Method1().ConfigureAwait(false);
await Method2().ConfigureAwait(false);
// Do something else
// ...
await Method3().ConfigureAwait(false);
await Method4().ConfigureAwait(false);

但是在这种情况下,只是像这样一开始就重置上下文就不太清楚了

await Task.Yield().ConfigureAwait(false);

它保证下面的代码将在没有同步上下文的情况下执行,不是吗?

即,我读到,如果该方法立即返回,则一次编写ConfigureAwait可能不起作用。对我来说,显而易见的解决方案看起来像对肯定不会立即返回的东西调用ConfigureAwait(false),哪个Task.Yield是,对吧?

我也知道Task.Yield不再包含ConfigureAwait(不知道为什么,因为我以前知道它曾经拥有过),但是查看Task.Yield代码,编写自己的方法非常容易除了用空的同步上下文调用延续之外,将无能为力。

对我来说,阅读似乎更容易,尤其是一次写时

await TaskUtility.ResetSyncContext();

而不是在每一行上写ConfigureAwait。

会工作(Task.Yield()。ConfigureAwait(false)或类似的自定义方法)还是我错过了什么?

加布里埃尔·卢西(Gabriel Luci)

通常,是的。除非该方法需要其上下文,否则应将ConfigureAwait(false)用于每次等待。

我经常在Stack Overflow上看到这些建议,甚至是Stephen Cleary(Microsoft MVP)在Async and Await文章中说的:

一个好的经验法则是使用ConfigureAwait(false),除非您知道确实需要上下文。

Stephen绝对知道他的知识,并且我同意该建议在技术上是准确的,但是我一直认为这是一个糟糕的建议,原因有两个:

  1. 初学者,以及
  2. 维修风险

首先,这对初学者是个坏建议,因为同步上下文是一个复杂的主题。如果您开始学习async/await被告知“除非该方法需要其上下文,否则ConfigureAwait(false)应用于所有await情况”,但您甚至不知道“上下文”是什么以及“需要”的含义,那么您就不需要知道什么时候不应该使用它,所以最终总是使用它。这意味着,除非您碰巧得知,否则您将遇到很难发现的错误,是的,您实际上确实需要“上下文”内容,而这种神奇的“ ConfigureAwait”内容使您迷失了它。您可能会花费数小时试图找出答案。

对于任何类型的应用程序,我相信建议确实应该相反:完全不要使用ConfigureAwait,除非您知道它的用途,并且确定在此行之后绝对不需要上下文。

但是,根据之后调用的方法,确定您是否不需要上下文可以很简单,也可以很复杂。但即便如此-这是第二个原因我那个建议不同意-只是因为你并不需要这一行后的背景下,现在,这并不意味着一些代码不会在以后添加,将使用的上下文。您必须希望进行更改的人知道做什么ConfigureAwait(false),看到并删除。ConfigureAwait(false)在任何地方使用都会产生维护风险。

这是另一位Stephen,Stephen Toub(Microsoft雇员)在“何时应使用ConfigureAwait(false)?”标题下的ConfigureAwait FAQ中建议的

编写应用程序时,通常需要默认行为(这就是为什么它是默认行为)。...这导致以下一般指导:如果您正在编写应用程序级代码,请不要使用ConfigureAwait(false)

在我自己的应用程序代码中,我不必费心尝试找出可以使用和不能使用它的地方。我只是忽略了那个ConfigureAwait存在。当然,可以通过使用它来提高性能,但是我真的怀疑这对任何人来说都不会有明显的区别,即使可以通过计时器来衡量。我不认为投资回报率是正的。

唯一的例外是在编写库时,正如Stephen Toub在他的文章中指出的那样:

如果您正在编写通用库代码,请使用 ConfigureAwait(false)

这有两个原因:

  1. 一个库不知道正在使用它的应用程序的上下文,因此它无论如何都不能使用上下文,并且
  2. 如果使用该库的人决定同步等待您的异步库代码,则可能导致死锁,使他们无法更改,因为他们无法更改您的代码。(理想情况下,他们不应该这样做,但是可能会发生)

要解决您的问题中的另一点:ConfigureAwait(false)在第一个await而不是其余的使用并不总是足够的。在您的库代码中的每一个 上使用它awaitStephen Toub在标题“在我的方法中仅在第一次等待而不在其余等待中使用ConfigureAwait(false)可以吗?”的文章。说,部分:

如果await task.ConfigureAwait(false)涉及到在等待时已经完成的任务(这实际上是非常普遍的),则这ConfigureAwait(false)将是没有意义的,因为线程在此之后继续在该方法中执行代码,并且仍在与之前相同的上下文中执行。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

ufw有什么优势,真的需要吗?

为什么我需要在所有传递闭包中使用ConfigureAwait(false)?

为什么我们需要Memento模式中的看守课程?真的那么重要吗?

为什么我的请求总是带有空值

为什么我不能在Elastic Beanstalk上编写带有.ebextensions的文件?

为什么我总是需要SUDO

什么是bogofilter,我需要吗?

在C ++中,是否真的不可能跳过带有默认参数的模板参数,为什么语法建议否则呢?

單行上的 ConfigureAwait(false)

Angular 5和Webpack-我真的需要吗?

为什么我们需要argc而argv末尾总是有一个null?

为什么我们总是需要检查对象是否具有键?-JS

NodeJS集群,真的需要吗?

为什么我的 getColor 函数总是返回 false?

为什么我的struct方法总是返回false?

为什么AS告诉我`(ob instanceof Button)`总是`false`?

为什么我的 UserControl Visible 属性总是返回 false?

为什么我的收费接收器总是返回false?

在检查列表项时为什么我总是得到 False

为什么 ArrayList 上的 contains() 方法总是返回 false?

为什么我总是需要去 grub 并进入 Ubuntu?

我想要一个全新的 linux 安装,所有这些分区都需要吗?为什么有一个 999GB 的?

为什么总是有东西在我的 mac 上的 5000 端口运行

我为什么要麻烦使用Task.ConfigureAwait(continueOnCapturedContext:false);

linux-image-extra软件包有什么用,我需要吗?

为什么我需要“ as_default()”在Tensorflow图中编写指令?

我可以对dbContext使用Async / Await吗?如果是这样,什么时候应该使用ConfigureAwait(false)?

类型转换INT浮动分裂之前。其中石膏我真的需要,我可以删除,为什么?

为什么在每条JavaDoc行上都加一个星号前缀?