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

什么时候该用 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) - ⚠️ 注意:
ApplicationStopping的CancellationToken会在超时(默认 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的朋友可以参考下。应用程序基础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 —— 此时回调看似“没执行”,其实是执行了一半就被强杀了。









