我清楚地了解 C# 中的 TAP 模型执行。 然而,当涉及到并发任务时,我感到困惑。
考虑 MS 文档中的这个例子:
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Task<Bacon> baconTask = FryBaconAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
Console.WriteLine("Breakfast is ready!");
在没有
async
ing的情况下调用await
这非常令人困惑。
让我们以这行为例:
Task<Egg> eggsTask = FryEggsAsync(2);
1- 主线程将调用该函数并开始执行它。
2-当主线程计数第一个
await
时,它将返回到Main()
函数。
现在,当
await Task.Delay(3000);
中的FryEggsAsync()
完成时发生了什么?
private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay(3000);
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay(3000);
Console.WriteLine("Put eggs on plate");
return new Egg();
}
我尝试用 Rider 调试这个例子,当
await Task.Delay(3000);
完成时我得到了 FryEggsAsync()
从线程池中生成线程并继续执行它。
这是否意味着这三个函数中的每一个都将使用池中的线程在后台继续执行,而不需要在主线程上继续执行?
或者功能在
await
行暂停并且直到我await
它从Main()
功能才继续?
Task
本身并没有说明并发性。它只是代表一个操作,可能有一个结果。它可能已经完成,可能在未来完成,也可能永远不会完成。它并没有真正说明线程的任何内容。在许多情况下,它将代表一个异步 IO 操作,根本不使用任何线程来运行。
await
本质上说,当任务完成时,继续执行。并在您开始时的相同“上下文”中执行此操作。即如果从 UI 线程调用,则为 UI 线程;如果从线程池调用,则为某个线程池线程。请注意,控制台程序没有任何 UI 线程,因此唯一的上下文是线程池上下文。
Task.Delay(3000);
本质上只是 Threading.Timer
的包装。所以在延迟之后操作系统将向线程池发送消息,请在一些可用线程上运行此代码。线程池将这样做,该代码将完成任务。接下来会发生什么取决于每个等待者的捕获上下文。
这是否意味着这三个函数中的每一个都将使用池中的线程在后台继续执行,而不需要在主线程上继续执行?
由于示例使用控制台程序,所以没有 UI 线程。有一个主线程,但它仍然使用线程池上下文。所以等待之后的任何事情都可能在线程池线程上被调用。这是有效的,因为它使用
Task Main
,即一旦主要任务完成,程序将终止。
现在,当 await Task.Delay(3000);在 FryEggsAsync() 中完成正在发生的事情?
延续被调用(即
await Task.Delay(3000)
之后的所有内容)。在控制台应用程序的“简单”/默认情况下,它将在线程池上调用,但是当存在SynchronizationContext
时有一些警告。
这是否意味着这三个函数中的每一个都将使用池中的线程在后台继续执行,而不需要在主线程上继续执行?
如果是示例控制台应用程序 - 是的。
或者该函数在 await 行挂起并且在我从 Main() 函数等待它之前不会继续?
没有。
await
在 Main
需要返回执行 Main 的线程控制,以防 Main
需要等待执行任务的完成。
阅读更多:
async
包括编译器生成状态机的描述我不认为任何人都可以责怪你对此感到困惑!
您链接到的文档中没有提到它,但我发现并行运行两个任务的最简单方法是 Parallel.Invoke()。 更多文档.
您可以使用以下方式异步运行任务:
Parallel.Invoke(() =>
{
AMethod();
},
async () =>
{
await AnotherMethod();
}
);