未声明Main方法为async,为何Task仍能异步运行?
未标记async的Main方法中,Task.Run启动的任务为何仍能异步运行?
我编写了如下代码,忘记将Main方法声明为async,因此原本认为producer和consumer任务会同步运行。
using System.Collections.Concurrent; namespace MainProgram; public class Program { public static void Main(string[] args) { using var intCollection = new BlockingCollection<int>(); int sum = 0; Task.Run(() => { Console.WriteLine("Running Producer"); Thread.Sleep(1000); for (int i = 0; i < 10; i++) { intCollection.Add(i); Console.WriteLine(i); } intCollection.CompleteAdding(); }); Task.Run(() => { Console.WriteLine("Running Consumer"); Thread.Sleep(1000); while (!intCollection.IsCompleted) { sum += intCollection.Take(); Console.WriteLine("Thingi Thingi"); } }); Console.WriteLine("aaabbb"); Console.ReadLine(); } }
但代码的实际输出如下:
aaabbb Running Producer Running Consumer 0 Thingi Thingi 1 Thingi Thingi 2 Thingi Thingi 3 Thingi Thingi 4 Thingi Thingi 5 Thingi Thingi 6 Thingi Thingi 7 8 9 Thingi Thingi Thingi Thingi Thingi Thingi
这让我感到意外,因为这些任务似乎在异步运行,请问有人能解释这一现象吗?
核心原因:Task.Run的本质是启用线程池线程并行执行
Task.Run()的作用是把传入的委托放到线程池的空闲线程上执行,它本身会立即返回,不会阻塞当前调用线程(也就是这里的主线程)。不管调用它的方法是不是标记了async,这个行为都不会变。- 你的Main方法里,调用两次
Task.Run()后,立刻就执行了Console.WriteLine("aaabbb"),这是因为主线程没有被阻塞,继续往下走了。而两个Task.Run()启动的生产者、消费者任务,会在各自的线程池线程上独立运行,和主线程并行。 - 最后
Console.ReadLine()让主线程保持存活,否则Main方法执行完毕后程序会直接退出,你就看不到生产者和消费者的后续输出了。
补充说明输出的细节
- 生产者和消费者都有
Thread.Sleep(1000),所以它们会在主线程输出aaabbb之后,差不多同时开始执行。 BlockingCollection<int>的Take()方法会阻塞当前线程,直到集合中有元素可取,所以大部分时候生产者添加一个元素,消费者就能立刻取出并输出,这就是你看到的0和Thingi Thingi交替出现的原因。- 最后生产者一次性添加了7、8、9,此时消费者可能还在处理前面的元素,等消费者再次调用
Take()时会连续取出这三个元素,所以最后会连续输出三次Thingi Thingi。
内容的提问来源于stack exchange,提问作者Armaho
相关产品推荐
相关产品推荐

