【问题标题】:Await returns the control to caller- who executes synchronous code in the awaited task?等待将控制权返回给调用者——谁在等待的任务中执行同步代码?
【发布时间】:2021-07-23 11:31:46
【问题描述】:

当遇到等待时,控制权返回给调用者,而等待的任务在后台运行(发出/等待网络请求/响应)。我知道等待任务在等待网络响应时不需要线程,因为它实际上并没有运行任何东西,只是在等待。

我想问一下-假设在awaited函数中,有一些像Console.WriteLine这样的同步代码,那么因为控制权已经回到了调用者,谁来执行console.writeline?

假设按钮点击事件发生await Fn1();

示例 1:

async Task<string> fn1() {
    string result = await NetworkCallAsync('http......');
    return result;
}

在上面的例子中遇到await Fn1(),控制权回到UI线程,同时进行网络调用。我明白这一点。当网络调用完成后,Ui 线程将执行剩下的代码。那就是 - 获取结果并返回。

示例 2:

async Task<string> fn1() {
    Console.WriteLine("hello"); //or any other synchronous code like Thread.Sleep(10);
    string result = await NetworkCallAsync('http......');
    return result;
}

在上面的代码中,当按钮点击遇到await Fn1()并且已经将控制权返回给UI线程,那么谁执行Console.WriteLine(或Thread.Sleep(10))如上例所示)?

【问题讨论】:

标签: c# async-await


【解决方案1】:

任务不一定需要任何线程来执行。一个任务可能代表一个 IO 操作,即磁盘或网络访问。如果是这样,任务可以使用操作系统中的异步功能来避免在等待时使用任何线程。

如果您自己开始一个任务,您可以指定一个taskScheduler 来确定要使用的线程。通常是操作系统线程或任意线程池线程。

任务也可以同步完成,要么因为结果已经可用,要么因为方法需要实现 API,使用真正的异步 IO 工作量很大。

作为任务的用户,您通常不需要关心任务是如何运行的,只需关心它是否已完成。

【讨论】:

  • 假设在 awaited 函数中有一些代码步骤(如 console.write),还有另一个 await,那么在将控制权传递给调用者之前,同步代码会执行吗?
  • @variable 方法调用将在当前线程上继续,直到第一次等待。但是对未知代码做出许多假设是不明智的。您需要阅读任何异步代码规范的文档。
  • 在我的例子中,第一个等待是按钮点击事件中的await f1()。那么你的意思是说呼叫将持续到最后一个等待?正如你在上面的评论中所说的第一次等待,我很困惑。
  • @variable, Fn1() 将像任何其他函数一样被调用。这将正常运行WriteLineNetworkCallAsync。只有在NetworkCallAsync return 之后,等待部分才起作用。到时候 if 会检查任务是否完成,如果没有完成,则返回一个新的任务,代表方法的结果。
  • 所以你的答案应该说 - 最终等待?
【解决方案2】:

Console.WriteLine("hello"); 将在调用 Fn1() 的同一线程上调用。在执行Fn1 内第一个await 之前的所有代码之前,Fn1() 不会返回Task&lt;string&gt;Console.WriteLine("hello"); 行位于第一个 await 之前,因此它将在调用线程上同步运行。

【讨论】:

  • 我经常在等待时读取控制权交给调用者。他们没有说会发生什么:1。嵌套等待,2.当嵌套等待中有同步代码后跟异步代码时。很高兴知道仅仅因为有等待它不会将控制权返回给调用者。它将在 await asyncFn() 之前执行同步代码并转到最内部的嵌套等待,然后在等待最内部的等待时才将控制权返回给调用者。控制将一直到调用者直到主线程。然后,当最里面的 await 准备好时,控制会转到上一级的 await
  • @variable await 确实将控制权返回给调用者(前提是此时等待未完成),但Fn1() 调用出现在await 之前。 await 是一个运算符,所以它需要一些东西来操作,并且必须首先创建一些东西。例如,将其与int result = - GetValue() 进行比较。您不会期望在调用GetValue 方法之前 调用减号运算符。为什么你会期待await
  • 我想补充的另一件事是,人们常说,当 async fn 在没有等待的情况下被调用时,控制会立即转到下一行,而无需等待 async 函数中的代码完全的。假设如果在 async fn 中有一些同步代码在等待之前,这是不正确的。现在在 asyncfn 中遇到 await 时,它将控制权交给调用者,调用者不会等待,因为没有 await。我要指出的是,仅仅因为没有 await 并不意味着这行下面的代码会立即运行。
  • @variable 他们说的原因是因为通常异步方法不包含同步阻塞代码。 Microsoft says: "基于 TAP 的异步方法可以在返回结果任务之前同步完成少量工作,例如验证参数和启动异步操作。同步工作应保持在最低限度所以异步方法可以快速返回。"
  • 好吧,最后一个等待我实际上是想说最后一个嵌套等待。无论如何,我现在明白了很多。感谢您花时间回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
  • 2013-08-06
  • 2021-10-31
  • 2014-09-06
  • 1970-01-01
  • 2022-01-04
相关资源
最近更新 更多