即使在 await 之后什么也没有,是否会创建一个延续?

Is a continuation created even if there is nothing after an await?

我正在读这个 article 并找到这个例子:

public static class DeadlockDemo
{
  private static async Task DelayAsync()
  {
    await Task.Delay(1000);
  }
  // This method causes a deadlock when called in a GUI or ASP.NET context.
  public static void Test()
  {
    // Start the delay.
    var delayTask = DelayAsync();
    // Wait for the delay to complete.
    delayTask.Wait();
  }
}

The root cause of this deadlock is due to the way await handles contexts. By default, when an incomplete Task is awaited, the current “context” is captured and used to resume the method when the Task completes. This “context” is the current SynchronizationContext unless it’s null, in which case it’s the current TaskScheduler. GUI and ASP.NET applications have a SynchronizationContext that permits only one chunk of code to run at a time. When the await completes, it attempts to execute the remainder of the async method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the async method to complete. They’re each waiting for the other, causing a deadlock.

我理解如果在 await Task.Delay(1000); 之后有代码,它将是延续的一部分,而延续将是 运行 在与 Test() 相同的上下文中,那就是死锁是如何发生的。

但是没有continue,那么死锁到底是怎么发生的呢?

或者,是否创建了一个空的延续?那有什么意义呢?

这是让我困惑的部分:

When the await completes, it attempts to execute the remainder of the async method within the captured context.

什么是“异步方法的剩余部分”?

如果我们是 async Taskasync Task<T> 方法,那么在 await 之后总是有工作要做——我们需要确保 [=12] 产生的任何异常=]ed 任务正确传播到我们自己的 Task,或者我们传递适当的正常 return 值。

如果我们是任何类型的 async 方法,使用诸如 using 的结构,它在我们的方法的一个否则会出现空尾声的末尾插入编译器生成的代码,那么我们将在方法末尾插入代码,即使它没有出现在源代码中。

如果我们是任意使用 awaits 的普通 async 方法,那么我们已经构建了一个状态机并且 运行 执行我们的方法,没有意义优化“最后一次等待后无代码”的可能性。

narrow 的情况下,我们是一个 async void1 方法,它包含一个 await最后,除了一些关于任务未处理异常可能被报告的细节之外,我们已经有机会通过根本不创建方法 async 并忽略可等待的方法来避免过多的代码生成。

所以没有理由尝试优化这种情况。


1无论如何,我们那时已经处于罪恶状态。