I understand that it's recommended to use ConfigureAwait(false)
for await
s 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.
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 }); }
What are the differences between these two approaches?
As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use ConfigureAwait false. This is simple, easy and can improve the performance of an application by freeing the UI thread for a little longer.
If we set 'ConfigureAwait(true)' then the continuation task runs on the same thread used before the 'await' statement. If we set 'ConfigureAwait(false)' then the continuation task runs on the available thread pool thread.
ConfigureAwait in Action You capture the current context before awaiting the task, leaving it to the task context, then recovering (re-entering) it back when the task completes.
In this video we answer the ever popular question “Which do I use, ConfigureAwait True or False?”. The direct answer to this question is: – If you are a writing code for the UI, use ConfigureAwait(true).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With