简体   繁体   中英

Use ConfigureAwait(false) or Task.Run to avoid blocking UI Thread

I'm trying to find out which approach is better let's say we have a button, after user clicks it we perform 1. Send a async request using httpClient 2. Some heavy synchronous staff like computations and saving data to a database.

Like that:

button1.Click += async(sender, e) => 
{
    bool a = await Task.Run(async () => { return await MyTask1();});
}
async Task<bool> MyTask1()
{
    await new HttpClient().GetAsync("https://www.webpage.com");
    DoHeavyStuffFor5minutes();
    return true;
}
button2.Click += async(sender, e) => 
{
    bool a = await MyTask2();
}
async Task<bool> MyTask2()
{
    await new HttpClient().GetAsync("https://www.webpage.com").ConfigureAwait(false);
    DoHeavyStuffFor5minutes();
}

From what i understand GetAsync does not block my UI thread because underneath it uses a method which make it runs on different thread perhaps Task.Run or any other method that allows that. But DoHeavyStuffFor5Minutes will block my UI because it will get called on the caller SynchornizationContext . So i read that using ConfigureAwait(false) will make code after GetAsync do not run on the same SynchornizationContext as the caller. My question is, which approach is better first or the second one?

There is no need to execute HttpClient.GetAsync on a background thread using Task.Run since the HTTP request is truly asynchronous by nature so in this case your second approach is better that the first one.

When the Task returned by GetAsync has eventually finished, the remainder or MyTask2() will be executed on a thread pool thread assuming you opt out of capturing the context by calling ConfigureAwait(false) .

Note however that ConfigureAwait(false) does not guarantee that the callback or remainer won't be run in the original context in all cases.

From Stephen Toub's blog post :

Does ConfigureAwait(false) guarantee the callback won't be run in the original context?

"No. It guarantees it won't be queued back to the original contex...but that doesn't mean the code after an await task.ConfigureAwait(false) won't still run in the original context. That's because awaits on already-completed awaitables just keep running past the await synchronously rather than forcing anything to be queued back. So, if you await a task that's already completed by the time it's awaited, regardless of whether you used ConfigureAwait(false) , the code immediately after this will continue to execute on the current thread in whatever context is still current."

So you might want to off-load DoHeavysTuffFor5minutes , which I assume is a CPU-bound and potentially long-running operation, to a background thread using Task.Run to be on the safe side. At least in the general case.

Also note that a method that is named *Async and returns a Task or Task<T> might still block the calling thread depending on its implementation. In general, this may be a reason to use your first approach of a calling both methods on a background thread in order to avoid blocking the UI thread. If you however use well-implemented APIs, such as HttpClient , this isn't an issue though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM