简体   繁体   English

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

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

I was reading this article and found this example:我正在阅读这篇文章并找到了这个例子:

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.这种死锁的根本原因是 await 处理上下文的方式。 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.这个“上下文”是当前的 SynchronizationContext,除非它是 null,在这种情况下它是当前的 TaskScheduler。 GUI and ASP.NET applications have a SynchronizationContext that permits only one chunk of code to run at a time. GUI 和 ASP.NET 应用程序有一个 SynchronizationContext,它一次只允许运行一段代码。 When the await completes, it attempts to execute the remainder of the async method within the captured context.当 await 完成时,它会尝试在捕获的上下文中执行异步方法的其余部分。 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.他们每个人都在等待对方,造成僵局。

I understand that if there was code after await Task.Delay(1000);我知道如果await Task.Delay(1000);之后有代码, it would be part of the continuation, and that continuation would be running in the same context as Test() , and that's how the deadlock would happen. ,它将成为延续的一部分,并且该延续将在与Test()相同的上下文中运行,这就是死锁发生的方式。

However, there is no continuation, so how does the deadlock actually happen?但是,没有继续,那么死锁实际上是如何发生的呢?

Or, is there an empty continuation that is created?或者,是否创建了一个空的延续? What would be the point in that?那有什么意义呢?

This is the part that is confusing me:这是让我困惑的部分:

When the await completes, it attempts to execute the remainder of the async method within the captured context.当 await 完成时,它会尝试在捕获的上下文中执行异步方法的其余部分。

What is the "remainder of the async method"?什么是“异步方法的其余部分”?

If we're an async Task or async Task<T> method, then there's always work to do after the await - we need to ensure that any exception produced by the await ed task is properly propagated to our own Task , or that we pass the appropriate normal return value through.如果我们是async Taskasync Task<T>方法,那么在await之后总是有工作要做——我们需要确保await ed 任务产生的任何异常都正确传播到我们自己的Task ,或者我们通过适当的正常返回值通过。

If we're any kind of async method using structures such as using which insert compiler-generated code at the end of an otherwise appearing empty epilogue to our method, then we'll be inserting code at the end of the method, even if it doesn't appear in the source.如果我们是任何一种使用结构的async方法,例如using which 在我们的方法出现的空结尾处插入编译器生成的代码,那么我们将在方法的末尾插入代码,即使它没有出现在源中。

If we're any normal async method that liberally uses await s then we'll already have a state machine built and running to execute our method and there's no point in optimizing the "no code after the last await" possibility.如果我们是自由使用await的任何普通async方法,那么我们已经构建并运行了 state 机器来执行我们的方法,并且没有必要优化“最后一次等待后没有代码”的可能性。

In the narrow case that we're an async void 1 method that contains a single await at the end, then other than some minutiae about where an unhandled exception from a task might be reported, we already had the opportunity to avoid excess code generation by not making the method async at all and just ignoring the awaitable.狭窄的情况下,我们是一个async void 1方法,最后包含一个await ,那么除了一些关于可能报告任务未处理异常的细节之外,我们已经有机会避免过多的代码生成根本不使方法async ,而只是忽略可等待的方法。

So there's no reason to try to optimize this situation.所以没有理由尝试优化这种情况。


1 We're already in a state of sin at that point anyway. 1无论如何,那时我们已经陷入了 state 的罪恶之中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM