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

时间:2013-02-16 01:49:04

标签: c# .net .net-4.5 async-await c#-5.0

我理解建议在库代码中使用ConfigureAwait(false)作为await,以便后续代码不会在调用者的执行上下文中运行,后者可能是一个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 个答案:

答案 0 :(得分:68)

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

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

答案 1 :(得分:16)

在这种情况下,您的Task.Run版本会有更多的开销,因为第一个等待调用(await client.GetAsync(address))仍将编组回调用上下文,{{Task.Run 1}}来电。

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

答案 2 :(得分:3)

同意@Stephen的回答,如果仍然混淆,请参见下面的截图 1#没有ConfigureAwait(false)
见下图图像主线程试图更新标签 enter image description here

2#With ConfigureAwait(false)
请参阅下面的图像工作线程尝试更新标签 enter image description here

答案 3 :(得分:1)

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

一种可能的解决方案是使用here中的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
}