.NET BackgroundService停止后重启抛出System.OperationCanceledException问题
BackgroundService重启报错原因及正确方式
问题原因
- .NET的
IHost实例是单次生命周期设计,调用StopAsync()后,宿主会完成内部资源释放、终止所有托管服务,并将自身状态标记为已停止/已处置。此时宿主内部的取消令牌、服务容器等核心组件已处于终止状态,无法再次启动,调用StartAsync()必然抛出System.OperationCanceledException。 - 原代码中
host.RunAsync()未使用await,会导致宿主启动逻辑与后续停止/重启逻辑并行执行,加剧状态冲突,但核心问题仍是IHost不可复用。
正确重启方式
不能复用同一个IHost实例,每次重启都需要销毁旧宿主,重新构建新的宿主实例并启动。修改后的代码示例如下:
Program.cs
while (true) { // 每次循环创建全新的宿主实例 using IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<TestWorker>(); }) .Build(); // 启动宿主并等待其运行 var hostTask = host.RunAsync(); Thread.Sleep(5000); await host.StopAsync(); // 等待宿主完全停止并释放资源 await hostTask; Thread.Sleep(5000); }
TestWorker.cs(保持不变)
public class TestWorker : BackgroundService { private readonly ILogger<TestWorker> _logger; public TestWorker(ILogger<TestWorker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); } } }
关键说明
- 使用
using声明宿主,确保每次循环结束后自动释放宿主资源,避免内存泄漏。 - 每次重启都重新构建
IHost,保证每次启动都是全新的生命周期,彻底规避旧实例的状态污染。 await hostTask确保宿主完全停止并完成资源释放后,再进入下一次重启循环,保证状态切换的完整性。
内容的提问来源于stack exchange,提问作者Xorcist
相关产品推荐
相关产品推荐

