【问题标题】:ConfigureAwait , UI, await async配置等待,用户界面,等待异步
【发布时间】:2021-01-28 08:55:55
【问题描述】:

我有一个小项目 - WinForms 在 .net 框架上 - 只是一个小测试:

private void button9_Click(object sender, EventArgs e)
{
    string text = GetTitleAsync().Result;
    button9.Text = text;            
}
private async Task<string> GetTitleAsync()
{
    await Task.Delay(3000);
    return "Hello!";
}

当我运行应用程序时, 单击按钮:“button9” - 导致死锁, (因为线程挂在“.result”上)

以这种方式编写 GetTitleAsync():

private async Task<string> GetTitleAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Hello!";
}

解决了死锁 - 应用程序运行正常。

但我不明白怎么做?

我曾预料到,使用“.ConfigureAwait(false)”会导致 情况:

"button9.Text = text;"在不同的线程上执行 在其上创建 UI 的那个, 并且会抛出异常!

但效果很好! 怎么样??

【问题讨论】:

  • Don't Block on Async Code -- 从那里删除 .ConfigureAwait(false);,更改:private async void button9_Clickstring text = await GetTitleAsync();
  • ConfigureAwait(false); 导致继续在不同的 Thread 上恢复,但 WindowsFormsSynchronizationContextstrong,第一个 await 将在用户界面线程。 Result 也是如此。但是你不会阻塞异步代码。
  • 我还建议阅读 Stephen Cleary 关于该主题的帖子(第一条评论中的链接)。我还必须在这个答案中解释它:How ConfigureAwait(false) Prevent UI Deadlocks。您可能会发现它很有用。

标签: winforms async-await configureawait


【解决方案1】:

我曾预料到,使用“.ConfigureAwait(false)”会导致“button9.Text = text;”的情况在与创建 UI 的线程不同的线程上执行,并且会抛出异常!但效果很好!怎么样??

我推荐阅读我的async/await intro;我尝试将您需要了解的有关 async/await 及其上下文的所有信息都包含在内,而不会介绍太多细节。

具体来说,该帖子有两点值得注意:

  • 每个async 方法都开始在调用线程上同步执行。
  • await 捕获上下文,除非您使用 ConfigureAwait(false)

所以,浏览这段代码:

private void button9_Click(object sender, EventArgs e)
{
    string text = GetTitleAsync().Result;
    button9.Text = text;            
}

private async Task<string> GetTitleAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Hello!";
}

这是按顺序发生的,特别注意哪个线程运行哪个代码:

  1. button9_Click 在 UI 线程上调用 GetTitleAsync()
  2. GetTitleAsync() 调用 Task.Delay(3000) 并返回一个将在 3 秒内完成的任务。
  3. GetTitleAsync() 调用 ConfigureAwait(false) 并取回一个不会在当前 (UI) 上下文中恢复的已配置等待程序。
  4. GetTitleAsync() 使用 await 异步等待任务完成。此 await 不会在当前 (UI) 上下文中恢复,因为 await 已配置为不恢复。
  5. await 检查任务并发现它不完整,因此它向其调用者返回一个不完整的Task&lt;string&gt;
  6. button9_Click 在该任务上调用 .Result。这会阻塞 UI 线程,直到该任务完成(即,GetTitleAsync() 已完成执行)。
  7. 三秒后,从Task.Delay(3000) 返回的任务完成。
  8. GetTitleAsync() 在其 await 之后继续执行。由于这是一个已配置的 await,因此它会继续在线程池线程上执行。
  9. GetTitleAsync() 返回 "Hello!"。这是在线程池线程上完成的。
  10. 通过返回一个值,GetTitleAsync() 现在完成了,它之前返回的 Task&lt;string&gt; 现在完成了一个结果值。这种完成也发生在线程池线程上。
  11. 由于任务现已完成,UI 线程不再被阻塞,它继续执行button9_Click
  12. button9_Click 在 UI 线程上执行 button9.Text = text;

【讨论】:

  • 非常感谢您的详细解释,对我来说是莫大的荣幸!我崇拜你,因为你渊博的知识和善良。
猜你喜欢
  • 2016-07-07
  • 2016-03-25
  • 2017-10-09
  • 1970-01-01
  • 1970-01-01
  • 2017-12-11
  • 1970-01-01
相关资源
最近更新 更多