C#。如果 "after await" 线程忙会怎样?

C#. What happens if "after await" thread is busy?

当等待任务完成但启动异步方法的线程不可用(例如处理另一个请求)时,C# 中会发生什么情况?然后将使用另一个线程而不是第一个线程,或者执行将等到繁忙线程可用?

提前感谢您的回答。

这取决于捕获的SynchronizationContext

  1. 在最简单的情况下没有 SynchronizationContext(例如控制台应用程序)。在这种情况下,延续不会在原始线程中调用,而是在线程池线程中调用。通常,线程池线程将可用,并将 运行 立即继续。否则,当线程池线程可用时,继续将 运行。

  2. 如果 SynchronizationContext 存在(例如 Windows Forms 应用程序),将根据特定 SynchronizationContext 的规则调用延续。例如,WindowsFormsSynchronizationContext 通过 UI 线程中的 windows 消息循环安排继续。如果 UI 线程恰好空闲,continuation 将立即 运行。如果 UI 线程繁忙,将安排在 UI 线程空闲的时刻继续。


更新: 根据@GR7 的要求,我应该补充一点,可以通过使用方法 ConfigureAwait。默认情况下 SynchronizationContext 被捕获,所以如果您不想被捕获,您可以将 await 配置为 ConfigureAwait(false)。如果 SynchronizationContext 不存在(例如控制台应用程序),则 ConfigureAwait 无效。

这取决于安排延续的线程的 SynchronizationContext

例如,当您在具有 UI 线程的应用程序(如 ASP.NET 或 WPF 应用程序)中使用 async/await 时,在 UI线程也会在UI线程上执行。在控制台应用程序中,没有 SynchronizationContext 被捕获,默认行为是在必须恢复执行时在任何可用的线程上执行。如果您考虑一下,在 "whatever" 线程上执行实际上比在安排继续的完全相同的线程上执行要容易得多。

所有这些只是部分正确,因为您可以配置一个 await 调用,通过在返回的 Task 上调用 ConfigureAwait(false) 来不捕获当前 SynchronizationContext awaiting.

为了说明这一点,请注意如果在 ASP.NET 应用程序中您在 UI 线程上开始异步工作然后强制它阻塞直到该工作完成,例如,您的代码可能会死锁通过在返回的 Task 上调用 Task.Result。现在您有一个必须在 UI 线程上执行的延续,但是 UI 线程正在等待该延续执行,因此 none 将永远继续。如果您在控制台应用程序中执行相同操作并且线程池中有空闲线程,则代码不会阻塞,因为它可以在 "whatever" 线程上免费执行。调用 ConfigureAwait(false) 后任何应用程序都会发生同样的情况 - 因为不会捕获 SynchronizationContext

TL;DR:您实际上问了一个相当简单的问题,但答案却极其复杂。简而言之:允许在任何线程上继续执行,除非 SynchronizationContext 强制它执行其他操作。进入更多细节会将这个答案变成一个相当大的博客 post,比我聪明得多的人已经制作了关于这个的博客 post,所以我将 link 你有关该主题的更多资源:

Stephen Toub's FAQ about ConfigureAwait

Stephen Cleary's detailed MSDN article

Stephen Toub's "Await, SynchronizationContext, and Console Apps"

Stephen Cleary about ASP.NET Core SynchronizationContext

Stephen Cleary's "Don't Block on Async Code"

What does SynchronizationContext do?

你可以通过ConfigureAwait来控制是否等待原线程

除非您在 WinForms 或 WPF 应用程序的 UI 线程中等待,否则您应该将 .ConfigureAwait(false) 附加到您的 await 调用,这样框架就不会等待原始线程,而是在第一个可用线程上继续工作。

查看 Stephen Toub 最近撰写的有关 ConfigureAwait 的文章 -- ConfigureAwait FAQ

另请参阅另一个 SO 问题 -- Best practice to use ConfigureAwait for all server side code