【问题标题】:Are awaits in async method called without await still asynchronous?在没有等待的情况下调用异步方法中的等待仍然是异步的吗?
【发布时间】:2019-08-15 12:02:29
【问题描述】:

考虑以下代码:

public static void Run() {
    DoStuffAsync();
}

public static async Task DoStuffAsync() {
     PerformCalc();
     await DoLongTaskAsync();
     PerformAnotherCalc();
}

假设我打电话给Run()。我有几个关于行为的问题:

  1. PerformCalc() 是否会在与调用Run() 的线程相同的线程上被同步调用?
  2. DoLongTaskAsync() 会被异步调用还是同步调用?换句话说,PerformAnotherCalc() 会/可以在DoLongTaskAsync() 完成之前被调用吗?
  3. 接下来,DoStuffAsync()方法能否在DoLongAsyncTask()执行完成之前返回?

【问题讨论】:

  • 1.是的。 2. 没有。 3. 它可以(或将假设DoLongTaskAsync 确实是异步的并返回一个未完成的任务)return 但不是complete
  • 你可以写一个小测试应用程序自己看看。调用签名中具有 async 的方法而不调用 await 与没有 async 的方法相同。像任何常规的 c# 方法一样。

标签: c# asynchronous async-await task


【解决方案1】:

异步方法总是开始同步运行。魔法发生在await,但只有当await 被赋予一个尚未完成的Task 时。

在您的示例中,当您调用 Run() 时会发生这种情况:

  1. 跳转到DoStuffAsync()
  2. 跳转到PerformCalc()
  3. 跳转到DoLongTaskAsync()
  4. 如果DoLongTaskAsync() 是一个真正的异步操作并返回一个不完整的Task,那么await 完成它的工作,DoStuffAsync() 将一个不完整的Task 返回到Run()
  5. 由于没有等待任务,Run() 完成。
  6. DoLongTaskAsync() 完成时,DoStuffAsync() 继续,并跳转到PerformAnotherCalc()

所有这些可以发生在同一个线程上。

所以回答你的问题:

  1. 是的。如果它是一个async 方法,它可能最终会出去并在其他线程上做事。但它会在同一个线程上同步启动。
  2. DoLongTaskAsync() 将被异步调用,但在 DoLongTaskAsync() 完成之前不会调用 PerformAnotherCalc(),因为您使用了 await
  3. 是的。这就是await 的工作原理。它将返回一个不完整的Task(也就是说,只有当DoLongTaskAsync() 是真正异步的并且返回一个不完整的Task 时)。然后,一旦 DoLongTaskAsync() 完成,DoStuffAsync() 的执行就会从中断处继续。

【讨论】:

  • 好答案。一个小提示:异步方法总是开始同步运行相对于调用者。这很重要,因为如果调用者已经在异步运行,它将与调用者同步,但不一定与调用调用者的调用者同步。很确定这就是您的意思,但可能有助于避免混淆。
【解决方案2】:
  1. 是否会在与调用 Run() 的线程相同的线程上同步调用 PerformCalc()?

是的。

  1. DoLongTaskAsync() 是异步调用还是同步调用?换句话说,PerformAnotherCalc() 会在 DoLongTaskAsync() 完成之前被调用吗?

它将被同步调用,但它可能会在“长任务”操作完成之前返回Task。无论哪种方式,它返回的Task 都处于等待状态,因此在从DoLongTaskAsync 返回的Task 完成之前,不会调用PerformAnotherCalc

  1. 随后,DoStuffAsync() 方法能否在 DoLongAsyncTask() 执行完成之前返回?

DoStuffAsync 方法将在遇到第一个 await 时返回(如果正在等待的 Task 处于等待状态)。这就是async 方法的工作原理——它们同步运行直到Task 的第一个await 处于挂起状态,然后它们返回一个Task,当整个方法执行完毕后,该Task 将完成。

如果DoLongTaskAsync 返回一个已经完成的Task:在这种情况下,DoStuffAsyncPerformAnotherCalc 返回之前不会返回。如果DoLongTaskAsync 返回一个仍待处理的Task,则DoStuffAsync 将在该点返回,并且它将返回一个Task,一旦从DoLongTaskAsync 返回的Task 完成,它就会完成,并且 PerformAnotherCalc 已经返回。

【讨论】:

    【解决方案3】:
    1. 是的
    2. 没有
    3. 是的

    另外,如果您考虑这些方法:

    public static void Run1() {
        DoStuffAsync();
        Console.WriteLine("Done");
    }
    
    public static async Task Run2() {
        await DoStuffAsync();
        Console.WriteLine("Done");
    }
    
    public static async Task DoStuffAsync() {
         PerformCalc();
         await DoLongTaskAsync();
         PerformAnotherCalc();
    }
    

    Run2 中,您保证在PerformAnotherCalc 之后会显示“完成”。在Run1中,“Done”会显示在PerformCalc之后,PerformAnotherCalc之前(假设DoLongTaskAsync实际上是异步的,这取决于它的实现)。

    【讨论】:

    • 您可以查看代码下方的注释。看起来你把他和你的例子混为一谈了
    • @JeroenvanLangen 我混合了这两个例子是的。在我的答案中添加了缺失的部分,以使其更容易理解
    【解决方案4】:

    所有调用都是同步的。如果返回的可等待对象不完整,则 await 可以是异步的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 2019-03-03
      相关资源
      最近更新 更多