简体   繁体   English

使用 ConfigureAwait(false) 或 Task.Run 避免阻塞 UI 线程

[英]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.我试图找出哪种方法更好假设我们有一个按钮,在用户单击它后我们执行 1. 使用httpClient发送异步请求 2. 一些繁重的同步人员,例如计算和将数据保存到数据库。

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.据我了解, GetAsync不会阻止我的 UI 线程,因为在它下面使用了一种使其在不同线程上运行的方法,可能是Task.Run或任何其他允许的方法。 But DoHeavyStuffFor5Minutes will block my UI because it will get called on the caller SynchornizationContext .但是DoHeavyStuffFor5Minutes会阻止我的 UI,因为它会在调用者SynchornizationContext上被调用。 So i read that using ConfigureAwait(false) will make code after GetAsync do not run on the same SynchornizationContext as the caller.所以我读到使用ConfigureAwait(false)将使GetAsync之后的代码不会在与调用者相同的 SynchornizationContext 上运行。 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.无需使用Task.Run在后台线程上执行HttpClient.GetAsync ,因为 HTTP 请求本质上是真正异步的,因此在这种情况下,您的第二种方法比第一种方法更好。

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) .GetAsync返回的Task最终完成时,剩余部分或MyTask2()将在线程池线程上执行,假设您通过调用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.但是请注意, ConfigureAwait(false)并不能保证回调或剩余程序在所有情况下都不会在原始上下文中运行。

From Stephen Toub's blog post :来自 Stephen Toub 的文:

Does ConfigureAwait(false) guarantee the callback won't be run in the original context? ConfigureAwait(false) 是否保证回调不会在原始上下文中运行?

"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." “不。它保证它不会排队回到原始上下文......但这并不意味着等待任务之后的代码await task.ConfigureAwait(false)仍不会在原始上下文中运行。那是因为等待已经完成的可等待对象只是保持同步运行超过await ,而不是强制任何东西排队返回。因此,如果您await在等待时已经完成的任务,无论您是否使用ConfigureAwait(false) ,紧随其后的代码无论上下文仍然是当前的,这将继续在当前线程上执行。”

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.因此,为了安全起见,您可能希望使用Task.RunDoHeavysTuffFor5minutes (我认为这是一个受 CPU 限制且可能长时间运行的操作)卸载到后台线程。 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.另请注意,名为 *Async 并返回TaskTask<T>的方法可能仍会阻塞调用线程,具体取决于其实现。 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.通常,这可能是您使用在后台线程上调用这两种方法的第一种方法以避免阻塞 UI 线程的原因。 If you however use well-implemented APIs, such as HttpClient , this isn't an issue though.但是,如果您使用实现良好的 API,例如HttpClient ,那么这不是问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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