首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ConfigureAwait和GetAwaiter的变化行为

ConfigureAwait和GetAwaiter的变化行为
EN

Stack Overflow用户
提问于 2019-03-11 11:23:53
回答 1查看 284关注 0票数 1

我在试验任务。我有过

代码语言:javascript
复制
private async Task<string> GetStringWithInnerCallConfigureAwaitFalseAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Finished!";
}

private async Task<string> GetStringAsync()
{
    await Task.Delay(3000);
    return "Finished!";
}

我觉得奇怪的是:

代码语言:javascript
复制
private void Button10_Click(object sender, RoutedEventArgs e)
{
    Button10.Content = "GetAwaiter() GetResult() + deadlock";
    var task = GetStringAsync().ConfigureAwait(false).GetAwaiter();
    var result = task.GetResult(); // deadlock
    Button10.Content = result;
}

我期望在最后一行没有死锁崩溃,因为它不是使用GetAwaiter(),但是我经历了死锁。

下一步:

代码语言:javascript
复制
private void Button11_Click(object sender, RoutedEventArgs e)
{
    Button11.Content = "GetAwaiter() GetResult() No deadlock";
    var task = GetStringWithInnerCallConfigureAwaitFalseAsync().ConfigureAwait(false).GetAwaiter();
    var result = task.GetResult(); // No deadlock
    Button11.Content = result; // No crash
}

在这里,我希望在最后一行崩溃,因为非UI上下文,但它的工作没有问题。当我们使用ConfigureAwait()时,GetAwaiter(False)没有意义吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-11 11:25:26

ConfigureAwait在同一个表达式中配置await关键字的行为。它不会影响其他方法中的其他await语句,如果不使用await,则不会产生任何影响。

它控制await语句是否捕获当前的SynchronizationContext (如果有)。实际上,如果在UI线程上运行await语句,那么await task;也会在UI线程上的await之后运行代码,而await task.ConfigureAwait(false)将在ThreadPool线程上运行await之后的代码。

在第一个示例中:

代码语言:javascript
复制
private async Task<string> GetStringWithInnerCallConfigureAwaitFalseAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Finished!"; // <-- Run on the thread pool
}

private async Task<string> GetStringAsync()
{
    await Task.Delay(3000);
    return "Finished!"; // <-- Run on the captured SynchronizationContext (if any)
}

这里的行为有一个不同之处,那就是运行return语句的线程。

在第一个方法中,当Task is awaited完成时,会向线程池发送消息,线程池运行return语句。

在第二个方法中,await语句捕获当前的SynchronizationContext (它引用UI线程),并使用它在上面运行return语句。这意味着一条消息在3秒后被发送到UI线程,告诉它运行该return语句。

在第一个调用GetStringAsync的片段中

代码语言:javascript
复制
var task = GetStringAsync().ConfigureAwait(false).GetAwaiter();

ConfigureAwait的调用在这里什么也不做,因为结果不是await。你可以在没有改变的情况下移除它。

代码语言:javascript
复制
var result = task.GetResult(); // deadlock

return中运行GetStringAsync语句需要UI线程。由于在对GetResult()的调用中阻止了它,所以它无法完成GetStringAsync方法,因此出现了死锁。

在调用GetStringWithInnerCallConfigureAwaitFalseAsync的第二个片段中

代码语言:javascript
复制
var task = GetStringWithInnerCallConfigureAwaitFalseAsync().ConfigureAwait(false).GetAwaiter();

同样,对ConfigureAwait(false)的调用什么也不做,因为您没有对结果进行await操作。

代码语言:javascript
复制
var result = task.GetResult(); // No deadlock

这一次,GetStringWithInnerCallConfigureAwaitFalseAsync执行await ...ConfigureAwait(false),因此在线程池而不是UI线程上运行await之后的代码。因此,不需要UI线程来完成此方法,因此您可以安全地(!)堵住它。

代码语言:javascript
复制
Button11.Content = result; // No crash

您在UI线程上调用了这个方法,但是您从来没有离开过它--同步调用所有东西,没有await,等等。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55100790

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档