You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 00:50:15