在IHostedService.StartAsync中调用StopApplication引发未处理TaskCanceledException
调用IHostApplicationLifetime.StopApplication后触发TaskCanceledException的问题分析与解决
问题场景
在.NET 7 WebAPI项目中实现IHostedService,当在StartAsync方法里调用IHostApplicationLifetime.StopApplication()触发应用关闭时,控制台会抛出未处理的TaskCanceledException。排查后确认该异常并非业务代码引发,而是在StartAsync返回后,由WebApplication/Host框架内部产生,导致应用无法优雅关闭。
环境信息
- WebApi net7.0
- Dotnet版本:7.0.100
控制台异常信息
Application is shutting down... Unhandled exception. System.Threading.Tasks.TaskCanceledException: A task was canceled. at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken) at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host) at Program.<Main>$(String[] args) in /.../Program.cs:line 29
复现步骤
- 创建新的WebApi项目
- 实现
IHostedService,在StartAsync中调用StopApplication() - 将该
IHostedService通过AddHostedService注册到服务集合
复现代码
Program.cs
var builder = WebApplication.CreateBuilder(args); builder.Services.AddHostedService<HostedService>(); // 其他配置... var app = builder.Build(); // 中间件配置... app.Run();
HostedService.cs
public class HostedService : IHostedService { private readonly IHostApplicationLifetime _hostApplicationLifetime; public HostedService(IHostApplicationLifetime hostApplicationLifetime) { _hostApplicationLifetime = hostApplicationLifetime; } public Task StartAsync(CancellationToken cancellationToken) { _hostApplicationLifetime.StopApplication(); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; }
问题原因
当在HostedService的StartAsync中直接调用StopApplication()时,宿主会立即启动关闭流程。此时Kestrel服务器还在执行端口绑定的异步操作,关闭信号会触发绑定任务的取消,进而抛出TaskCanceledException。而.NET 7的框架没有对这种主动关闭引发的取消异常做捕获处理,导致异常暴露为未处理异常,中断优雅关闭流程。
解决方案
方案1:在启动入口捕获TaskCanceledException
修改Program.cs的启动逻辑,捕获启动过程中因主动关闭引发的取消异常:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddHostedService<HostedService>(); // 其他配置... var app = builder.Build(); // 中间件配置... try { app.Run(); } catch (TaskCanceledException) { // 忽略主动关闭导致的取消异常,不影响优雅关闭 }
或者使用异步启动方式:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddHostedService<HostedService>(); // 其他配置... var app = builder.Build(); // 中间件配置... try { await app.RunAsync(); } catch (TaskCanceledException) { // 无需处理,属于主动关闭的预期行为 }
方案2:延迟触发StopApplication到启动完成后
如果业务允许等待应用启动完成再执行关闭,可以将StopApplication()的调用注册到ApplicationStarted事件中:
public Task StartAsync(CancellationToken cancellationToken) { _hostApplicationLifetime.ApplicationStarted.Register(() => { _hostApplicationLifetime.StopApplication(); }); return Task.CompletedTask; }
这种方式下Kestrel已完成端口绑定,关闭流程不会触发绑定任务的取消,自然不会抛出异常。
方案3:升级到.NET 8
该问题在.NET 8中已被官方修复,框架会自动处理启动过程中主动关闭引发的取消异常,无需额外代码处理。如果项目允许升级,这是最彻底的解决方式。
内容的提问来源于stack exchange,提问作者dev01
相关产品推荐
相关产品推荐

