为何C#中Parallel.ForEach及Task.Run内的Console.WriteLine无输出?
问题原因与解决方案
核心原因
控制台程序的主线程(执行Main方法的线程)运行结束后,整个进程会直接终止,所有后台线程会被强制停止。你用Task.Run启动的是后台任务,主线程在打印完Main program后立刻退出了,导致Task里的代码还没被调度执行,自然看不到标记2和3的输出。
解决方案
要让主线程等待后台任务完成后再退出,有两种常用方式:
方式1:使用Task.Wait()
把Task.Run返回的Task实例保存下来,调用Wait()方法让主线程等待后台任务执行完毕:
public static class Program { private static void Main(string[] args) { List<string> urls = new List<string> { "https://www.microsoft.com", "https://www.google.com", "https://www.amazon.com" }; // 替换为线程安全集合,避免Parallel.ForEach多线程写入冲突 ConcurrentBag<string> Results = new ConcurrentBag<string>(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 1"); // 保存后台任务实例 var backgroundTask = Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 2"); // HttpClient建议复用,避免频繁创建导致端口耗尽 using HttpClient client = new HttpClient(); Parallel.ForEach<string>(urls, (url) => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 3"); Console.WriteLine(url); var content = client.GetStringAsync(url).Result; Results.Add(url + " " + content.Length); Console.WriteLine(url + " " + content.Length); }); }); Console.WriteLine("Main program"); // 主线程等待后台任务完成 backgroundTask.Wait(); } }
方式2:将Main改为异步方法
利用await关键字等待后台任务完成,需要把Main的返回类型改为Task:
public static class Program { // 改为async Task private static async Task Main(string[] args) { List<string> urls = new List<string> { "https://www.microsoft.com", "https://www.google.com", "https://www.amazon.com" }; ConcurrentBag<string> Results = new ConcurrentBag<string>(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 1"); // await等待后台任务完成 await Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 2"); using HttpClient client = new HttpClient(); Parallel.ForEach<string>(urls, (url) => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " 3"); Console.WriteLine(url); var content = client.GetStringAsync(url).Result; Results.Add(url + " " + content.Length); Console.WriteLine(url + " " + content.Length); }); }); Console.WriteLine("Main program"); } }
额外注意点
- 原代码中
List<string> Results不是线程安全集合,Parallel.ForEach的多线程环境下直接调用Add会引发线程安全问题,建议替换为ConcurrentBag<string>或其他线程安全集合。 HttpClient应该复用(比如声明为静态字段或在using块中创建一次),避免频繁创建实例导致系统端口耗尽。
内容的提问来源于stack exchange,提问作者MUSA BEYTEKİN
相关产品推荐
相关产品推荐

