使用ConfigureAwait(false)和Task.Run有什么区别?

Sam*_*Sam 55 .net c# async-await c#-5.0 .net-4.5

据我所知,它是推荐使用ConfigureAwait(false)await库码S,以便后续代码不会在调用者的执行上下文,这可能是一个UI线程上运行.我也明白await Task.Run(CpuBoundWork)应该使用而不是CpuBoundWork()出于同样的原因.

用例子 ConfigureAwait

public async Task<HtmlDocument> LoadPage(Uri address)
{
    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address).ConfigureAwait(false))
    using (var responseContent = httpResponse.Content)
    using (var contentStream = await responseContent.ReadAsStreamAsync().ConfigureAwait(false))
        return LoadHtmlDocument(contentStream); //CPU-bound
}
Run Code Online (Sandbox Code Playgroud)

用例子 Task.Run

public async Task<HtmlDocument> LoadPage(Uri address)
{
    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address))
        return await Task.Run(async () =>
        {
            using (var responseContent = httpResponse.Content)
            using (var contentStream = await responseContent.ReadAsStreamAsync())
                return LoadHtmlDocument(contentStream); //CPU-bound
        });
}
Run Code Online (Sandbox Code Playgroud)

这两种方法有什么不同?

Ste*_*ary 69

当你说Task.Run,你说你有一些CPU工作可能需要很长时间,所以它应该始终在线程池线程上运行.

当你说ConfigureAwait(false),你说的是该async方法的其余部分不需要原始上下文.ConfigureAwait更多的是优化提示; 它并不总是意味着继续在线程池线程上运行.

  • @rism:我指的是*不需要上下文的任何东西.在这种情况下,`LoadHtmlDocument`正在解析HTML; 这种解析不必在UI线程上完成,因此它不需要上下文. (6认同)
  • @cosset:好的.由于这是一个UI应用程序,需要上下文的一个示例是更新UI控件.例如,如果`async`方法的结尾更新了`Label`或`ProgressBar`,那么它将需要在UI上下文中恢复.如果没有,它将获得跨线程异常. (4认同)
  • 我正在http://blog.stephencleary.com/2012/02/async-and-await.html上阅读你的文章,我对ConfigureWait(false)感到有点困惑,所以在此结束.在这里你说"其余的异步方法不是原始的上下文",这对我来说有点澄清.你能举一个"不需要的背景"的例子,这样我就可以巩固我的理解吗? (2认同)

Ree*_*sey 16

在这种情况下,您的Task.Run版本将有更多的开销,因为第一个await call(await client.GetAsync(address))仍将封送回调用上下文,调用的结果也是如此Task.Run.

另一方面,在第一个示例中,您的第一个Async()方法被配置为不需要编组回调用上下文,这允许继续在后台线程上运行.因此,不会有任何编组回到调用者的上下文中.


Pan*_*wat 6

同意@Stephen的回答,如果仍然困惑,请参见下面的屏幕截图1#没有ConfigureAwait(false)
请参见下图主线程尝试更新标签 在此输入图像描述

2# 使用ConfigureAwait(false)
请参见下图工作线程尝试更新标签 在此输入图像描述