ConfigureAwait,UI,等待异步

ConfigureAwait , UI, await async

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

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 = 文本;"在不同的线程上执行 创建 UI 的那个, 并且会抛出异常!

但效果很好! 怎么样??

I would have expected, that using ".ConfigureAwait(false)" would cause a situation in which "button9.Text = text;" is executed on a different thread than the one, on which the UI was created, and an excpetion would be throwed ! but it works excellent ! how??

我建议阅读我的 async/await intro;我尽量包含您需要了解的关于 async/await 及其上下文的所有信息,但不会涉及太多细节以供介绍。

具体来说,post有两点值得注意:

  • 每个 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 检查任务并发现它未完成,因此它 returns 未完成 Task<string> 给它的调用者。
  6. button9_Click 在该任务上调用 .Result。这会阻塞 UI 线程,直到该任务完成(即,GetTitleAsync() 已完成执行)。
  7. 三秒后,从 Task.Delay(3000) 返回的任务完成。
  8. GetTitleAsync() 在其 await 之后恢复执行。由于这是配置的 await,它会继续在线程池线程上执行。
  9. GetTitleAsync()returns"Hello!"。这是在线程池线程上完成的。
  10. 通过返回一个值,GetTitleAsync() 现在完成了,它之前返回的 Task<string> 现在完成了一个结果值。此完成也发生在线程池线程上。
  11. 由于任务现已完成,UI线程不再阻塞,它继续执行button9_Click
  12. button9_Click 在 UI 线程上执行 button9.Text = text;