gpt4 book ai didi

c# - 对 c# 的 async/await 的控制流感到困惑

转载 作者:行者123 更新时间:2023-11-30 13:53:41 25 4
gpt4 key购买 nike

我正在学习 async/await ,并且对 MSDN 的 await 的解释感到困惑:" await 操作符暂停执行,直到 GetByteArrayAsync 方法的工作完成。同时,控制权返回给 GetPageSizeAsync 的调用者

我不明白的是,“返回”是什么意思?起初,我相信当一个线程(比如 UI 线程)到达“await”关键字时,系统会创建一个新线程(或从 threadPool 获取一个线程)来执行其余的代码,并且 UI 线程可以返回调用者方法并执行其余部分。

但现在我知道“await”永远不会创建线程。

我写了一个演示:

class Program
{
static void Main(string[] args)
{
new Test().M1();
Console.WriteLine("STEP:8");
Console.Read();
}
}

class Test
{
public async void M1()
{
Console.WriteLine("STEP:1");
var t = M2();
Console.WriteLine("STEP:3");
await t;
}
public async Task<string> M2()
{
Console.WriteLine("STEP:2");
string rs = await M3();//when the thread reaches here,why don't it return to M1 and execute the STEP:3 ??
Console.WriteLine("STEP:7");
return rs;

}

public async Task<string> M3()
{
Console.WriteLine("STEP:4");
var rs = Task.Run<string>(() => {
Thread.Sleep(3000);//simulate some work that takes 3 seconds
Console.WriteLine("STEP:6");
return "foo";
});
Console.WriteLine("STEP:5");
return await rs;
}
}

这个演示打印了一些代表执行流程的标签,我认为它会是

第1步

第2步

步骤:3

第四步

步骤:5

步骤:6

步骤:7

步骤:8

(按顺序从 1 到 8),

但实际结果是:
Figure 1

第1步

第2步

第四步

步骤:5

步骤:3

步骤:8

步骤:6

步骤:7

如果像MSDN的解释一样,控制权返回到M1并打印“STEP 3”,我绝对错了,那么到底发生了什么?

提前致谢

最佳答案

只是为了摆脱它,不能保证在任何特定的 await除了继续执行其余代码之外,我们将做任何事情。但是,在 MSDN 中的示例和您自己的示例中,我们将始终在 await 处等待。点。

所以,我们到达了一个点,我们有 await z我们决定等待。这意味着 a) 那 z尚未完成(无论 z 完成意味着什么都不是我们现在关心的事情)和 b) 目前我们没有有用的工作要做。

希望通过上面的内容,您可以明白为什么“创建一个新线程”或任何类似的事情是没有必要的,因为就像我刚才所说的那样,没有任何有用的工作要做。

不过,在我们从我们的方法返回控制权之前,我们要让一个延续入队。 async机器能够表达“当 z 完成时,安排此方法的其余部分从我们的 await 点继续”。

在这里,诸如同步上下文和 ConfigureAwait变得相关。我们正在运行的线程(以及我们即将放弃控制的线程)在某些方面可能是“特殊的”。它可能是 UI 线程。它目前可能对某些资源具有独占访问权限(想想 ASP.Net pre-Core 请求/响应/ session 对象)。

如果是这样,希望提供特殊性的系统已经安装了同步上下文,并且通过它我们也能够获得“当我们恢复执行此方法时,我们需要具有与以前相同的特殊情况” .所以例如我们可以继续在 UI 线程上运行我们的方法。

默认情况下,在没有同步上下文的情况下,会找到一个线程池线程来运行我们的延续。

注意,如果一个方法包含多个 await需要等待的 s,在第一次等待之后,我们将作为链式延续运行。我们最初的调用者在我们第一次获得了他们的上下文 await编。

在您的样本中,我们有三个 await点,它们三个都会等待并导致我们将控制权交还给我们的调用者(而且我们在一个方法中没有多个 await 需要担心)。归根结底,所有这些等待都在等待 Thread.Sleep完成(现代 async 代码中的糟糕形式应该使用 TaskDelay 代替)。所以任何 Console.WriteLine s 出现在 await 之后在那个方法中会被延迟。

然而,M1async void ,最好避免在事件处理程序之外进行。 async void方法是有问题的,因为它们无法确定何时完成。
Main电话M1打印 1然后拨打 M2 . M2版画 2然后拨打 M3 . M3版画 4 , 创建一个新的 Task , 版画 5只有这样,它才能放弃控制。正是在这一点上,它创建了一个 TaskCompletionSource这将代表其最终完成,获得 Task从它,并返回。

请注意,仅当调用 M3 时返回我们击中了 awaitM2 .我们也必须在这里等,所以我们做的几乎和M3一样。并返回 Task .

现在 M1终于有了Task可以await在。但在此之前,它会打印 3 .它将控制权返回给 Main现在打印 8 .

万世之后,Thread.SleeprsM3运行完成,我们打印 6 . M3标记其返回 Task完成后,没有更多的工作要做,因此退出,没有进一步的延续安排。
M2现在可以恢复和它的await并打印 6 .这样做之后,它的 Task已完成并M1终于可以恢复打印7 .
M1没有 Task标记为完整,因为它是 async void .

关于c# - 对 c# 的 async/await 的控制流感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53706526/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com