如何让BackgroundService在Windows关机时接收通知?
在IHostedService中实现系统关机前操作的方案
你可以通过监听系统会话结束事件来区分正常服务停止和系统关机场景,避免在每次StopAsync执行时都运行关机逻辑。以下是针对Windows系统的具体实现方案,同时补充跨平台的思路:
方案一:Windows系统下监听SessionEnding事件
利用Microsoft.Win32.SystemEvents.SessionEnding事件,可以精准捕获系统关机或用户注销的触发时机,只在系统关机时执行目标操作。
代码实现
using Microsoft.Win32; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; public class ShutdownAwareBackgroundService : BackgroundService { private readonly ILogger<ShutdownAwareBackgroundService> _logger; public ShutdownAwareBackgroundService(ILogger<ShutdownAwareBackgroundService> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 常规后台任务逻辑 while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Service running at {Time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); } } public override Task StartAsync(CancellationToken cancellationToken) { // 注册系统会话结束事件 SystemEvents.SessionEnding += OnSessionEnding; return base.StartAsync(cancellationToken); } public override Task StopAsync(CancellationToken cancellationToken) { // 注销事件,避免内存泄漏 SystemEvents.SessionEnding -= OnSessionEnding; return base.StopAsync(cancellationToken); } private void OnSessionEnding(object sender, SessionEndingEventArgs e) { // 仅处理系统关机场景,排除用户注销 if (e.Reason == SessionEndReason.SystemShutdown) { _logger.LogInformation("System shutdown detected. Executing pre-shutdown operations..."); // 执行关机前操作(需同步执行,系统关机有超时限制) RunPreShutdownTasks(); } } private void RunPreShutdownTasks() { // 这里编写你的关机前逻辑:比如保存状态、关闭数据库连接、清理临时文件等 _logger.LogInformation("Pre-shutdown operations completed successfully."); } }
注意事项
- 仅支持Windows系统:该事件是Windows特有的API,跨平台场景需用其他方案。
- 操作需简短高效:系统关机时有严格的超时限制(通常几秒钟),避免执行耗时操作。
- 权限与服务配置:如果将服务作为Windows服务运行,需确保服务账户有足够权限执行目标操作,同时在Host配置中添加
.UseWindowsService():
var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<ShutdownAwareBackgroundService>(); }) .UseWindowsService() .Build(); await host.RunAsync();
方案二:跨平台场景的替代思路
对于Linux/macOS等跨平台环境,系统关机时会向进程发送SIGTERM信号,你可以通过以下方式处理:
- 监听
AppDomain.CurrentDomain.ProcessExit事件,该事件在进程即将终止时触发(包括系统关机)。 - 结合
IHostApplicationLifetime.ApplicationStopping事件,但需要额外逻辑区分手动停止和系统关机(比如通过环境变量或外部标记)。
不过这种方式无法像Windows的SessionEnding事件那样精准区分关机和其他停止原因,需根据业务场景权衡使用。
为什么StopAsync不合适?
StopAsync会在多种场景下触发:手动停止服务、服务重启、配置变更导致的服务重启等,无法直接判断是否是系统关机触发的停止操作,因此需要借助系统级事件来做精准区分,减少不必要的逻辑执行。
内容的提问来源于stack exchange,提问作者Jonathan Siegel
相关产品推荐
相关产品推荐

