0

0

c# ASP.NET Core 的 IHostApplicationLifetime 和应用生命周期

畫卷琴夢

畫卷琴夢

发布时间:2026-01-01 09:12:30

|

665人浏览过

|

来源于php中文网

原创

ASP.NET Core 3.0+ 必须使用 IHostApplicationLifetime,因 IApplicationLifetime 已被移除;它适用于所有 Generic Host 场景,其 ApplicationStarted、Stopping、Stopped 三阶段有明确触发时机和资源清理约束。

c# asp.net core 的 ihostapplicationlifetime 和应用生命周期

什么时候该用 IHostApplicationLifetime 而不是 IApplicationLifetime

ASP.NET Core 3.0+ 已彻底移除 IApplicationLifetime,所有新项目必须用 IHostApplicationLifetime。如果你在升级旧项目时发现 IApplicationLifetime 注入失败或类型找不到,不是配置问题,而是它已被废弃——直接换为 IHostApplicationLifetime 即可。

这个接口由 HostBuilder 创建的 IHost 提供,适用于所有基于 Generic Host 的场景(Web、Worker Service、Console Host),不局限于 Web 应用。

  • IHostApplicationLifetime.ApplicationStarted 在应用真正就绪、中间件链已构建完成、监听器已启动后触发(此时 HTTP 请求可被接收)
  • IHostApplicationLifetime.ApplicationStopping 在收到终止信号(如 Ctrl+C、dotnet stop、K8s SIGTERM)后立即触发,但仍在处理已有请求
  • IHostApplicationLifetime.ApplicationStopped 在所有 ApplicationStopping 回调执行完毕、所有 IAsyncDisposable 资源释放完成后才触发

ApplicationStopping 里能安全做哪些事?不能做哪些事?

这是最容易出错的阶段:你以为还有足够时间清理,其实没有。

  • ✅ 可以:取消长期运行的 CancellationTokenSource、关闭自定义后台任务、记录日志、调用 await _httpClient.PostAsync(...)(但必须设超时)
  • ❌ 禁止:阻塞等待未完成的 I/O(如无超时的 Task.Wait())、调用未设超时的同步网络请求、写入未缓冲的文件流(可能卡住)、依赖其他正在销毁的 Scoped 服务(它们可能已 Disposed)
  • ⚠️ 注意:ApplicationStoppingCancellationToken 会在超时(默认 5 秒)后强制触发 ApplicationStopped,不会等你手动完成
public class GracefulShutdownService : IHostedService
{
    private readonly IHostApplicationLifetime _lifetime;
    private readonly ILogger _logger;

    public GracefulShutdownService(IHostApplicationLifetime lifetime, ILogger logger)
    {
        _lifetime = lifetime;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _lifetime.ApplicationStopping.Register(OnStopping);
        return Task.CompletedTask;
    }

    private void OnStopping()
    {
        _logger.LogInformation("Application is stopping. Initiating graceful shutdown...");
        // ✅ 正确:用独立 token 控制超时,避免拖垮整个生命周期
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
        try
        {
            _cleanupTask?.Wait(cts.Token); // 假设 cleanupTask 是一个可等待的清理任务
        }
        catch (OperationCanceledException)
        {
            _logger.LogWarning("Cleanup timed out.");
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

为什么ApplicationStarted 里获取 IServiceScope 很危险?

因为此时 DI 容器虽已构建完成,但 Scoped 生命周期的服务尚未被任何请求激活,IServiceScope 的创建和释放机制还未进入常规轨道。强行在这里解析 Scoped 服务,可能导致:

Android开发指南中文pdf版
Android开发指南中文pdf版

Android开发指南中文pdf版,学习android的朋友可以参考下。应用程序基础Application Fundamentals 关键类 应用程序组件 激活组件:intent 关闭组件 manifest文件 Intent过滤器 Activity和任务 Affinity(吸引力)和新任务 加载模式 清理堆栈 启动任务 进程和线程 进程 线程 远程过程调用 线程安全方法 组件生命周期 Activity生命周期 调用父类 服务生命周期 广播接收器生命周期 进程与生命周期 用户界面User Interface

下载
  • 服务实例被意外提升为 Singleton(尤其在非 Web 主机中)
  • 数据库上下文(DbContext)提前初始化并持有连接,而后续请求可能复用或冲突
  • 某些依赖 HttpContext 的服务(如 IUrlHelper)抛出 InvalidOperationException: No service for type 'Microsoft.AspNetCore.Http.HttpContext' has been registered.

正确做法是:只在 ApplicationStarted 中触发「无依赖」的初始化逻辑(如预热缓存、加载配置快照),或改用 IHostedService.StartAsync —— 它在相同时机执行,但明确支持依赖注入和作用域管理。

Linux 容器环境下 ApplicationStopping 不触发的常见原因

Kubernetes 或 Docker 默认发送的是 SIGTERM,但 .NET 进程若未正确注册信号处理器,或被包装脚本拦截,会导致 ApplicationStopping 完全不执行。

  • 确认容器入口命令是 dotnet YourApp.dll,而非 sh -c "dotnet..." 等间接调用(会丢失信号传递)
  • 检查是否启用了 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 —— 极少数情况下会影响信号注册(罕见,但值得排查)
  • Dockerfile 中添加 STOPSIGNAL SIGTERM,并确保 docker stop 使用默认超时(10 秒),给 ApplicationStopping 留出时间
  • Program.cs 开头加日志验证信号捕获:
    Console.CancelKeyPress += (s, e) =>
    {
        Console.WriteLine("CancelKeyPress received");
        e.Cancel = true; // 防止进程立即退出,让 Host 自己处理
    };

最隐蔽的问题是:你在 ApplicationStopping 里 await 了一个没设超时的异步操作,而容器平台只等 10 秒就发 SIGKILL —— 此时回调看似“没执行”,其实是执行了一半就被强杀了。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

175

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

212

2025.12.18

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2025.12.29

console接口是干嘛的
console接口是干嘛的

console接口是一种用于在计算机命令行或浏览器开发工具中输出信息的工具,提供了一种简单的方式来记录和查看应用程序的输出结果和调试信息。本专题为大家提供console接口相关的各种文章、以及下载和课程。

410

2023.08.08

console.log是什么
console.log是什么

console.log 是 javascript 函数,用于在浏览器控制台中输出信息,便于调试和故障排除。想了解更多console.log的相关内容,可以阅读本专题下面的文章。

477

2024.05.29

k8s和docker区别
k8s和docker区别

k8s和docker区别有抽象层次不同、管理范围不同、功能不同、应用程序生命周期管理不同、缩放能力不同、高可用性等等区别。本专题为大家提供k8s和docker区别相关的各种文章、以及下载和课程。

249

2023.07.24

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.3万人学习

Git 教程
Git 教程

共21课时 | 2.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号