59

我了解建议在库代码中使用ConfigureAwait(false)for awaits,以便后续代码不会在调用者的执行上下文中运行,这可能是 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
}

示例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
        });
}

这两种方法有什么区别?

4

4 回答 4

73

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

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

于 2013-02-16T03:07:41.143 回答
16

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

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

于 2013-02-16T02:10:00.463 回答
3

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

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

于 2017-05-07T05:03:24.213 回答
1

附带说明一下,在这两种情况下仍然LoadPage()可能会阻塞您的 UI 线程,因为await client.GetAsync(address)需要时间来创建要传递给 ConfigureAwait(false). 在任务返回之前,您的耗时操作可能已经开始。

一种可能的解决方案是SynchronizationContextRemover这里使用:

public async Task<HtmlDocument> LoadPage(Uri address)
{
    await new SynchronizationContextRemover();

    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address))
    using (var responseContent = httpResponse.Content)
    using (var contentStream = await responseContent.ReadAsStreamAsync())
        return LoadHtmlDocument(contentStream); //CPU-bound
}
于 2017-03-24T10:51:16.570 回答