ConfigureAwait , UI, await async

问题描述

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

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

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

以这种方式编写 GetTitleAsync():

${DOCKER_MACHINE}" create -d virtualBox --virtualBox-no-vtx-check $PROXY_ENV "${VM}

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

但我不明白怎么做?

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

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

效果很好! 怎么样??

解决方法

我曾预料到,使用“.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<string>
  6. button9_Click 在该任务上调用 .Result。这会阻塞 UI 线程,直到该任务完成(即 GetTitleAsync() 已完成执行)。
  7. 三秒后,Task.Delay(3000) 返回的任务完成。
  8. GetTitleAsync() 在其 await 之后继续执行。由于这是一个配置的 await,它会继续在线程池线程上执行。
  9. GetTitleAsync() 返回 "Hello!"。这是在线程池线程上完成的。
  10. 通过返回一个值,GetTitleAsync() 现在已经完成,它之前返回的 Task<string> 现在已经完成并带有结果值。此完成也发生在线程池线程上。
  11. 既然任务现在已经完成,UI 线程不再被阻塞,而是继续执行 button9_Click
  12. button9_Click 在 UI 线程上执行 button9.Text = text;