You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 20:25:11