.NET 8.0 托管控制台应用Ctrl+C优雅终止实现咨询
.NET 8控制台应用优雅终止问题解决
问题背景
使用.NET 8.0构建控制台应用(基于2024年1月稳定包),需实现以下需求:
- 创建应用主机,完成Logging/DI/Configuration配置
- 支持单应用循环运行,正常退出时执行收尾逻辑
- 按下Ctrl+C时触发优雅终止流程,而非立即退出进程
当前代码正常运行可输出RunAsync done和stopped实现优雅关闭,但按下Ctrl+C时应用直接退出,无法触发终止逻辑。
原问题代码
public class Program { public static async Task Main(string[] args) { var hostBuilder = CreateDefaultApp(args); var host = hostBuilder.Build(); Console.WriteLine("starting"); var app = host.Services.GetRequiredService<TestApp>(); await app.RunAsync(); Console.WriteLine("stopped"); } private static IHostBuilder CreateDefaultApp(string[] args) { var builder = Host.CreateDefaultBuilder(); builder.ConfigureServices(conf => { conf.AddSingleton<TestApp>(); }); builder.ConfigureLogging(conf => { conf.ClearProviders(); conf.AddConsole(); }); builder.UseConsoleLifetime(); return builder; } } public class TestApp { private readonly IHostLifetime _lifetime; private readonly IHostApplicationLifetime _appLifetime; public TestApp(IHostLifetime lifetime, IHostApplicationLifetime appLifetime) { _lifetime = lifetime; _appLifetime = appLifetime; } public async Task RunAsync() { var stopToken = _appLifetime.ApplicationStopping; Console.WriteLine("RunAsync starting"); for (int i = 0; i < 10; i++) { if (stopToken.IsCancellationRequested) { Console.WriteLine("STOP"); break; } await Task.Delay(250); Console.Write(i); } Console.WriteLine(); Console.WriteLine("RunAsync done"); } }
解决方案
方案1:使用Hosted Service(官方推荐方式)
将业务逻辑封装为BackgroundService,主机启动时自动运行,原生支持生命周期管理与优雅终止:
public class Program { public static async Task Main(string[] args) { var hostBuilder = CreateDefaultApp(args); await hostBuilder.RunAsync(); // 直接运行主机,自动处理生命周期 } private static IHostBuilder CreateDefaultApp(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureServices(conf => { conf.AddHostedService<TestHostedService>(); // 注册托管服务 }) .ConfigureLogging(conf => { conf.ClearProviders(); conf.AddConsole(); }) .UseConsoleLifetime(); } } // 继承BackgroundService实现托管服务 public class TestHostedService : BackgroundService { private readonly ILogger<TestHostedService> _logger; public TestHostedService(ILogger<TestHostedService> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("RunAsync starting"); for (int i = 0; i < 10; i++) { if (stoppingToken.IsCancellationRequested) { _logger.LogInformation("STOP"); break; } await Task.Delay(250, stoppingToken); // 传递取消令牌,快速响应终止 _logger.LogInformation("{Count}", i); } _logger.LogInformation("RunAsync done"); } }
方案2:修改原代码启动主机
若不想使用Hosted Service,需手动启动主机以激活生命周期监听:
public class Program { public static async Task Main(string[] args) { var hostBuilder = CreateDefaultApp(args); var host = hostBuilder.Build(); // 启动主机,开始监听Ctrl+C等生命周期信号 await host.StartAsync(); Console.WriteLine("starting"); var app = host.Services.GetRequiredService<TestApp>(); await app.RunAsync(host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStopping); // 停止主机并释放资源 await host.StopAsync(); Console.WriteLine("stopped"); } private static IHostBuilder CreateDefaultApp(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureServices(conf => { conf.AddSingleton<TestApp>(); }) .ConfigureLogging(conf => { conf.ClearProviders(); conf.AddConsole(); }) .UseConsoleLifetime(); } } public class TestApp { public async Task RunAsync(CancellationToken stopToken) { Console.WriteLine("RunAsync starting"); for (int i = 0; i < 10; i++) { if (stopToken.IsCancellationRequested) { Console.WriteLine("STOP"); break; } await Task.Delay(250, stopToken); // 传递取消令牌 Console.Write(i); } Console.WriteLine(); Console.WriteLine("RunAsync done"); } }
关键说明
- 原问题核心:未调用
host.StartAsync()或host.RunAsync(),导致ConsoleLifetime未初始化,无法捕获Ctrl+C信号,系统直接终止进程。 - Hosted Service是.NET主机生态的标准业务运行模式,自动处理启动/停止、取消令牌传递等逻辑,无需手动管理主机生命周期。
- 在异步操作中传递
CancellationToken,可让延迟、IO等操作快速响应终止请求,提升优雅终止的时效性。
内容的提问来源于stack exchange,提问作者Bryan B
相关产品推荐
相关产品推荐

