.NET Core异步多线程控制台应用Task.WhenAll后未输出控制台信息且应用提前关闭问题排查
问题分析与解决方案
你的核心问题是主线程提前退出,导致异步任务还没执行完毕,应用就被关闭了,所以Console.WriteLine("Finish running code")根本没机会执行。我们一步步拆解问题并修复:
问题1:主线程未等待异步任务完成
你的Main方法是void类型,调用异步方法时没有等待其完成:
host.RunAsync()是异步方法,但你没有await它,它会在后台启动主机,但主线程会继续往下走RunTask是同步方法,里面调用了async Task类型的Execute,但同样没有等待Execute完成- 当
Main方法执行完毕后,整个进程就会终止,所有未完成的异步任务都会被强制取消,这就是你看到application is shutting down的原因
问题2:不必要的Task.Run包裹异步方法
ProcessPendingRegistrationsAsync本身就是async Task方法,直接返回任务即可,不需要用Task.Run包裹——Task.Run在这里会创建一个嵌套任务,虽然最终也能执行,但属于多余的开销,还容易造成混淆。
修复后的代码示例
1. 修改Program.cs,让主线程等待异步任务完成
将Main改为async Task(.NET Core 3.0+支持异步入口方法),并等待所有任务执行完毕:
internal class Program { // 改为异步入口方法,让主线程可以等待异步操作 private static async Task Main(string[] args) { var log4NetConfiguration = new Log4NetConfiguration(); log4NetConfiguration.SetLog4NetConfiguration(); var host = CreateHostDefaultBuilder(args).Build(); // 启动主机并在后台运行 var hostBackgroundTask = host.RunAsync(); // 等待业务任务执行完成 await RunTask(host.Services); // 任务完成后,停止主机并等待其关闭 await host.StopAsync(); await hostBackgroundTask; } public static IHostBuilder CreateHostDefaultBuilder(string[] args) { return Host.CreateDefaultBuilder() .ConfigureAppConfiguration(app => { app.AddJsonFile("appsettings.json"); }) .ConfigureServices((_, services) => { services.ConfigureDAL().ConfigureBusiness().ConfigureConnectionHelper().ConfigureTimeOutSetting(); }); } // 改为异步方法,等待TaskHelper的Execute完成 public static async Task RunTask(IServiceProvider services) { var taskHelper = services.GetService<TaskHelper>(); if (taskHelper != null) { await taskHelper.Execute(true); } } }
2. 修改TaskHelper.cs,去掉多余的Task.Run
public class TaskHelper { private readonly IDRegistration _dRegistration; // 明确任务列表的类型 private List<Task<List<RegistrationData>>> _processPendingRegistrationsTasks = new(); // 依赖注入构造函数(确保你的服务已正确注册) public TaskHelper(IDRegistration dRegistration) { _dRegistration = dRegistration; } public async Task Execute(bool forceMode) { var openPendingRegistrationsPerPages = _dRegistration.GetAllRegistrationsByStatusAndSubStatusPerPage(cachedByGuid[1], cachedByGuid[2]); // 直接添加异步方法返回的Task,无需Task.Run foreach (var openPendingRegistrationsData in openPendingRegistrationsPerPages) { _processPendingRegistrationsTasks.Add(ProcessPendingRegistrationsAsync(openPendingRegistrationsData)); } if (_processPendingRegistrationsTasks.Count > 0) { await Task.WhenAll(_processPendingRegistrationsTasks); } // 现在这行代码会被正常执行 Console.WriteLine("Finish running code"); } private async Task<List<RegistrationData>> ProcessPendingRegistrationsAsync(List<RegistrationData> registrations) { // 在这里添加你的实际异步处理逻辑(比如数据库操作、API调用等) // 示例:await _someService.ProcessRegistrationAsync(registrations); return registrations; } }
修复说明
- 异步入口方法:
async Task Main让主线程能够等待所有异步操作完成,不会提前终止进程。 - 等待主机与业务任务:先启动主机(保证服务可用),再执行业务任务,任务完成后再关闭主机,确保整个流程完整。
- 移除多余的
Task.Run:异步方法本身返回Task,直接加入任务列表即可,Task.WhenAll会正确等待所有任务执行完毕。
内容的提问来源于stack exchange,提问作者mohammad hamid
相关产品推荐
相关产品推荐

