【问题标题】:Can synchronous methods continue their execution before an asynchronous sub-call has completed?同步方法可以在异步子调用完成之前继续执行吗?
【发布时间】:2021-07-29 12:47:14
【问题描述】:

我在理解有关 C# 中的异步编程的细节时遇到问题。

假设我有一个通用的异步方法,其中包含一些等待语句。假设我有另一个方法,它不是异步的,而是调用第一个方法。

public void DoSomething(){
    DoSomethingAsync()
    ...
}

public async Task DoSomethingAsync(){
    ...
    await ...
    ...
}

我知道我可以只使用Wait() 或其他一些同步技术,但我不只是想在不知道会发生什么的情况下抛出问题。

DoSomething 会在DoSomethingAsync 完成之前继续执行吗?我知道如果将DoSomething 声明为async,就会发生这种情况,但事实并非如此。

或者更笼统地说:同步方法可以在异步子调用完成之前继续执行吗?

【问题讨论】:

  • 要清楚 - async 对方法所做的唯一事情(通过一些手波)是允许您在方法中使用 await。没有其他的。如果它实际上不包含任何await 语句,它不会改变该方法的工作方式。
  • DoSomethingAsync 将同步运行到第一个 await。那时它将返回一个Task。请注意,根据 Damian,如果 DoSomething 被声明为 async会发生完全相同的事情——唯一的区别是您可以在生成的任务上使用 await

标签: c# asynchronous


【解决方案1】:

TL;DR 它继续

同步方法能否在 异步子调用完成了吗?

当然可以,如果同步方法必须等待异步方法完成,那么异步的目的是什么? (它也可以等待,这取决于你想要实现的目标)

您的代码中的async/await 模式将创建一个可以稍后恢复的状态机。当您的代码到达await 时,您await 上的任务将被推送到任务调度程序并可能在某个线程池线程中执行。 async 方法返回,让同步方法继续。

任务完成后,状态机恢复上下文,DoSomethingAsyncawait 之后的所有内容都会继续。它会在哪个线程上运行?这取决于您是否设置了 ConfigureAwait 和程序的线程模型。例如,对于 WPF 应用程序,默认情况下,它会返回到主线程,这就是为什么 wait + await 会导致死锁。

【讨论】:

    【解决方案2】:

    会发生什么:

    DoSomething 调用 DoSomethingAsync。 然后是同步的!订单开始,直到达到第一个“等待”。 通过这个等待,DoSomethingAsync 返回一个状态为“尚未完成”的任务对象。但是由于您没有分配此返回值,也没有做任何其他特别的事情,所以您的 DoSomething 会继续。

    【讨论】:

      【解决方案3】:

      在没有 await 操作符的情况下,此方法的行为就像没有等待此调用,在调用完成之前继续执行当前方法

      编译器会警告你:

      https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs4014?f1url=%3FappId%3Droslyn%26k%3Dk(CS4014)

      【讨论】:

        【解决方案4】:

        同步方法能否在异步子调用完成之前继续执行?

        正如其他人所指出的,这个问题的答案是“是”。

        所有asynchronous methods begin executing synchronously。它们返回一个表示该异步方法完成的任务。当异步方法完成时,任务就完成了。如果异步方法返回结果,则任务以结果值完成。如果异步方法抛出异常,则任务以异常完成(即故障)。

        但是,这意味着您发布的代码存在一些问题。具体来说,它在做一种“一劳永逸”,这是危险的。

        这段代码只是忽略DoSomethingAsync返回的任务:

        public void DoSomethingElse(){
            DoSomethingAsync()
            ...
        }
        

        所以这意味着它在说“开始做某事”并且它也是在说“我不在乎那个'某事'是否完成或何时完成,我不在乎它是否引发异常”。这有几个问题:1)由于您的应用程序不知道“某事”何时完成,因此它无法知道何时可以安全关闭,以及 2)如果“某事”失败,您的应用程序将不会收到通知.

        在绝大多数情况下,这意味着应用程序存在错误。 “一劳永逸”仅适用于执行操作是否失败或什至未完成实际上并不重要的操作。很少有这样的操作。

        这就是为什么 - 作为一般规则 - 任务最终应该是awaited。如果你想开始一个操作然后做其他事情然后await那个操作来完成,这并不少见:

        public async Task DoSomethingElseAsync() {
          var task = DoSomethingAsync();
          ... // other synchronous/asynchronous work.
          await task; // asynchronously wait for DoSomethingAsync to complete,
                      // and propagate any exceptions.
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-28
          • 1970-01-01
          相关资源
          最近更新 更多