提问人:Mohab Alnajjar 提问时间:2/28/2023 最后编辑:Mohab Alnajjar 更新时间:2/28/2023 访问量:224
任务如何并发运行?
How tasks can run concurrently?
问:
我清楚地了解 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
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()
答:
现在,当 await Task.Delay(3000);在 FryEggsAsync() 中完成发生了什么?
调用延续(即 之后的所有内容)。在控制台应用程序的“简单”/默认情况下,它将在线程池上调用,但存在时有一些警告。await Task.Delay(3000)
SynchronizationContext
这是否意味着这三个函数中的每一个都将使用池中的线程在后台继续执行,而无需在主线程上继续执行?
对于示例控制台应用 - 是的。
或者该函数在 await 行暂停,直到我从 Main() 函数等待它才继续?
不。 在需要等待执行任务完成的情况下,需要返回执行 Main 的线程控件。await
Main
Main
阅读更多:
- .NET 中的异步编程 - 简介、误解和问题 - 关于该主题的精彩文章
- 异步大师 Stephen Cleary 的 There Is No Thread(也阅读他的其他文章)
- SynchronizationContext 有什么作用?
- 使用 SynchronizationContext 时 async/await 死锁
- 剖析 C# 中的异步方法 - 深入了解编译器生成的状态机的描述
async
Task
它本身并没有说明并发性。它只是表示一个操作,可能有一个结果。它可能已经完成,将来可能完成,也可能永远不会完成。它并没有真正说明线程。在许多情况下,它将表示一个异步 IO 操作,该操作从不使用任何线程来运行。
await
本质上是说,当任务完成时,继续执行。并在与开始时相同的“上下文”中执行此操作。即,如果从 UI 线程调用,则为 UI 线程,如果从线程池调用,则为某个线程池线程。请注意,控制台程序没有任何 UI 线程,因此唯一的上下文是线程池上下文。
Task.Delay(3000);
本质上只是一个包装器。因此,在延迟后,操作系统将向线程池发送消息,请在某个可用线程上运行此代码。线程池将这样做,该代码将完成任务。接下来会发生什么将取决于每个等待者捕获的上下文。Threading.Timer
这是否意味着这三个函数中的每一个都将使用池中的线程在后台继续执行,而无需在主线程上继续执行?
由于该示例使用控制台程序,因此没有 UI 线程。有一个主线程,但它仍然使用线程池上下文。因此,await 之后的任何内容都可能在线程池线程上调用。这是有效的,因为它使用 ,即一旦主任务完成,程序将终止。Task Main
评论