.NET 6中BackgroundService的停止方式有哪些?
问题场景
我使用.NET 6开发了一个部署在Windows服务器上的REST API,通过继承BackgroundService实现了一个后台进程,仅自行实现了该类的ExecuteAsync方法,方法内包含一个使用cancellationToken的延迟循环。代码如下:
public async Task ExecuteAsync(CancellationToken cancellationToken) { try { ..... foreach (var number in numbers) { .... await Task.Delay(100, cancellationToken); ..... } ..... } catch (Exception e) { ........ _logger.LogError(e.GetBaseException(),"Failed processing requested numbers"); throw; } }
从日志捕获的异常可知,cancellationToken被触发取消进程,按官方文档这属于“优雅关闭”,但后续仍有大量请求日志显示应用还在运行。想了解BackgroundService除了优雅关闭外还有哪些停止方式。
核心说明
首先要明确:BackgroundService的ExecuteAsync方法触发取消,仅代表该后台服务实例停止,而非整个ASP.NET Core应用关闭。因为ASP.NET Core的主宿主(处理HTTP请求的核心进程)和后台服务是独立的宿主组件,后台服务停止不会主动终止主宿主。
BackgroundService的所有停止方式
1. 优雅关闭(默认触发方式)
- 触发时机:当应用宿主收到系统停止信号时(比如Windows服务停止指令、控制台按Ctrl+C、云平台部署停止命令),会向所有
IHostedService(包含BackgroundService)传递取消令牌。 - 行为:
ExecuteAsync中的cancellationToken被触发,Task.Delay会抛出TaskCanceledException;若该异常未被捕获(或捕获后重新抛出),后台服务会正常停止,但主宿主不会终止,应用仍可处理HTTP请求。
2. 未处理的非取消类异常终止
如果ExecuteAsync中抛出未被捕获的非取消类型异常(如业务异常、空引用异常等),且未在宿主配置中设置忽略该类异常,整个应用宿主会直接终止,同时停止所有服务。
- 注意:你当前代码中捕获异常后重新抛出,若抛出的是
TaskCanceledException,宿主不会终止;若为其他异常,宿主会停止。
3. 主动调用宿主停止方法
在应用内部,可通过依赖注入获取IHostApplicationLifetime,调用StopApplication()方法,直接触发整个宿主的停止流程,同时通知所有BackgroundService执行优雅关闭逻辑。
示例代码:
private readonly IHostApplicationLifetime _hostLifetime; public YourBackgroundService(IHostApplicationLifetime hostLifetime) { _hostLifetime = hostLifetime; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 满足特定条件时,主动终止整个应用 if (someCriticalCondition) { _hostLifetime.StopApplication(); return; } // 原有业务逻辑 try { foreach (var number in numbers) { await Task.Delay(100, stoppingToken); // ... } } catch (Exception e) { _logger.LogError(e.GetBaseException(), "Failed processing requested numbers"); // 若需要异常时终止应用,可在此调用StopApplication _hostLifetime.StopApplication(); } }
4. 系统层面强制终止
直接通过系统工具强制结束应用进程(比如Windows任务管理器结束进程、taskkill命令),这种方式不会触发BackgroundService的优雅关闭逻辑,属于暴力停止,可能导致数据未持久化、资源未释放等问题。
5. 宿主超时强制停止
在Program.cs中配置宿主的停止超时时间,若BackgroundService在超时时间内未完成优雅关闭,宿主会强制终止该后台服务实例(默认不终止整个应用,可通过配置调整):
builder.Host.ConfigureServices(services => { services.Configure<HostOptions>(options => { // 设置30秒的停止超时 options.ShutdownTimeout = TimeSpan.FromSeconds(30); }); });
针对你场景的建议
如果你希望后台服务停止时整个应用也随之关闭,可以在ExecuteAsync的异常处理逻辑中,判断异常类型(或业务条件),调用IHostApplicationLifetime.StopApplication()来终止整个宿主进程。
内容的提问来源于stack exchange,提问作者Daan

