61

I understand that it's recommended to use ConfigureAwait(false) for awaits in library code so that subsequent code does not run in the caller's execution context, which could be a UI thread. I also understand that await Task.Run(CpuBoundWork) should be used instead of CpuBoundWork() for the same reason.

Example with 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
}

Example with 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
        });
}

What are the differences between these two approaches?

4 Answers 4

76

When you say Task.Run, you are saying that you have some CPU work to do that may take a long time, so it should always be run on a thread pool thread.

When you say ConfigureAwait(false), you are saying that the rest of that async method does not need the original context. ConfigureAwait is more of an optimization hint; it does not always mean that the continuation is run on a thread pool thread.

15
  • 2
    I was reading your article at blog.stephencleary.com/2012/02/async-and-await.html and I got a bit confused around ConfigureWait(false) so ended here. Here you say "the rest of the async method does not the original context" which somewhat clarifies things for me. Tho can you pls give an example of a "not needed context" so I can solidify my understanding?
    – rism
    Jun 5, 2014 at 6:55
  • 6
    @rism: By that I mean anything that doesn't need the context. In this case, LoadHtmlDocument is parsing the HTML; this parsing doesn't have to be done on the UI thread, so it doesn't need the context. Jun 5, 2014 at 12:29
  • 1
    @Stephen Cleary,could you please provide example when the rest of async method need context?
    – vborutenko
    Jan 30, 2016 at 10:08
  • 4
    @cosset: Sure. Since this is a UI app, an example of needing context would be updating a UI control. For example, if the end of the async method updated a Label or ProgressBar, then it would need to resume on the UI context. If it didn't, it would get a cross-thread exception. Jan 30, 2016 at 12:38
  • 1
    @ThanasisIoannidis: I mean "context" as defined in my async intro: the current SynchronizationContext or TaskScheduler. It is a hint, but it will take effect if the task has not yet completed. It does not change contexts if the task is already completed (e.g., result was read from a cache). If you have code that must run on the thread pool, Task.Run is the more appropriate tool. Mar 22, 2017 at 13:32
17

In this case, your Task.Run version will have a bit more overhead, as the first await call (await client.GetAsync(address)) will still marshal back into the calling context, as will the results of the Task.Run call.

In the first example, on the other hand, your first Async() method is configured to not require marshaling back into the calling context, which allows the continuation to run on a background thread still. As such, there won't be any marshaling back into the caller's context.

6

Agreed @Stephen answer, If still confusion see below screenshots 1# Without ConfigureAwait(false)
See below image Main thread trying to update Label enter image description here

2# With ConfigureAwait(false)
See below image working thread trying to update label enter image description here

1

As a side note, in both cases LoadPage() could still block your UI thread, because await client.GetAsync(address) needs time to create a task to pass to ConfigureAwait(false). And your time consuming operation might have already started before task is returned.

One possible solution is to use SynchronizationContextRemover from here:

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
}
1
  • I tried this and it worked. My only question is. Am I always need to use await new sync..... in every thread that has await? or I only needed this on my httpclient class?
    – zackmark15
    Jul 30, 2020 at 18:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.