首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ConfigureAwait在.NET中的应用

ConfigureAwait在.NET中的应用
EN

Stack Overflow用户
提问于 2020-07-01 16:55:52
回答 3查看 10.4K关注 0票数 17

我在不同地方读过关于ConfigureAwait的文章(包括一些问题),下面是我的结论:

  • ConfigureAwait(true):在等待程序运行之前,在同一个线程上运行其余的代码。
  • ConfigureAwait(false):在等待的代码运行的同一个线程上运行其余的代码。
  • 如果等待后面跟着访问UI的代码,则任务应附加.ConfigureAwait(true)。否则,由于另一个线程访问UI元素,将出现InvalidOperationException。

我的问题是:

  1. 我的结论正确吗?
  2. ConfigureAwait(false)什么时候提高性能,什么时候没有?
  3. 如果为GUI应用程序编写,但下一行不访问UI元素。我应该使用ConfigureAwait(假)还是ConfigureAwait(真)?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-07-02 13:27:58

为了更直接地回答你的问题:

ConfigureAwait(true):在等待程序运行之前,在同一个线程上运行其余的代码。

不一定是同一个线程,而是相同的同步上下文。同步上下文可以决定如何运行代码。在UI应用程序中,它将是同一个线程。在ASP.NET中,它可能不是同一个线程,但是您将像以前一样拥有可用的HttpContext

ConfigureAwait(false):在等待的代码运行的同一个线程上运行其余的代码。

这不对。ConfigureAwait(false)告诉它它不需要上下文,所以代码可以在任何地方运行。它可能是运行它的任何线程。

如果等待后面跟着访问UI的代码,则任务应附加.ConfigureAwait(true)。否则,由于另一个线程访问UI元素,将出现InvalidOperationException。

“应该附加.ConfigureAwait(true)”是不正确的。ConfigureAwait(true)是默认的。所以如果这是你想要的,你不需要指定它。

  1. ConfigureAwait(false)什么时候提高性能,什么时候没有?

返回同步上下文可能需要时间,因为它可能需要等待其他东西才能完成运行。实际上,这种情况很少发生,或者等待时间太短,以至于你永远不会注意到这一点。

  1. 如果为GUI应用程序编写,但下一行不访问UI元素。我应该使用ConfigureAwait(假)还是ConfigureAwait(真)?

您可以使用ConfigureAwait(false),但我建议您不要使用,原因如下:

  1. 我怀疑你是否会注意到性能的提高。
  2. 它可以引入您可能不期望的并行性。如果使用ConfigureAwait(false),则该延续可以在任何线程上运行,因此,如果要访问非线程安全对象,则可能会出现问题。出现这些问题并不常见,但这是可能发生的。
  3. 您(或其他维护此代码的人)可能会添加稍后与UI交互的代码,并引发异常。希望ConfigureAwait(false)很容易被发现(它的方法可能不同于抛出异常的地方),并且您/他们知道它会做什么。

我发现完全不使用ConfigureAwait(false)会更容易(除了在库中)。用ConfigureAwait常见问题中的Stephen (微软雇员)的话来说

在编写应用程序时,通常需要默认行为(这就是为什么它是默认行为)。

编辑:我已经写了一篇关于这个主题的文章:.NET:不要使用ConfigureAwait(假)

票数 19
EN

Stack Overflow用户

发布于 2020-07-01 17:05:45

如果可用的工作线程不多,并且需要等待的线程总是繁忙的话,ConfigureAwait(false)可能会提高性能。

在不需要返回同一个SynchronizationContext (通常使用线程链接)的地方,推荐使用SynchronizationContext,特别是在内部等待的库中:https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f

当您需要相同的上下文时,需要ConfigureAwait(true) (这是默认的),但在某些情况下也可能导致死锁。

请考虑以下代码:

代码语言:javascript
复制
void Main()
{
    // creating a windows form attaches a synchronization context to the current thread
    new System.Windows.Forms.Form();
    var task = DoSth();
    Console.WriteLine(task.Result);
}

async Task<int> DoSth()
{
    await Task.Delay(1000);
    return 1;
}

在本例中,由于没有等待任务DoSth,主UI线程被等待task.Result阻塞-同时DoSth被阻塞,因为它想在延迟后返回到UI线程。这将导致死锁,这段代码将永远不会执行到最后。添加.ConfigureAwait(false)解决了这种情况下的问题。

票数 3
EN

Stack Overflow用户

发布于 2020-07-01 19:03:27

在应用程序代码中使用ConfigureAwait(false)通常不会以任何有意义的方式提高应用程序的性能,因为在应用程序代码中通常不会在循环中使用await。例如,假设您的应用程序有一个按钮,每次用户单击该按钮时都会启动一个异步操作,而异步操作包括一个await。通过在此.ConfigureAwait(false)之后键入22个字符await,您已经损失了您生命中的相应时间,您可以希望从10个用户中节省下来,这些用户每分钟单击该按钮一次,每天8小时,持续20年(总计35,000,000次上下文切换=某些秒的CPU处理时间)。

在考虑是否可以安全地包含此配置(取决于是否继续包含与UI相关的代码)、每次必须维护/修改代码时重新确认之前的评估所需的时间、以及在评估错误时调试所损失的时间之前,请考虑到这一点。

另一方面,如果Button_Click处理程序包含如下代码:

代码语言:javascript
复制
private async void Button_Click(object sender, EventArgs e)
{
    var client = new WebClient();
    using var stream = await client.OpenReadTaskAsync("someUrl");
    var buffer = new byte[1024];
    while ((await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
    {
        //...
    }
}

...then无论如何都要花费额外的时间来执行ReadAsync任务。还要考虑通过将流读取部分移动到一个单独的异步方法来重构代码,这样您就可以安全地访问Button_Click处理程序中任何地方的UI元素,而不会因为不属于应用程序这一层的技术细节而分心。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62681749

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档