.NET 6中如何从Hosted Service启动长期运行的Scoped Service
解决方案:在.NET 6中让Manager HostedService与Scoped Client同时运行
你的问题核心是直接await了Client的DoWork方法,而DoWork是一个无限循环任务,导致Manager的执行流程被阻塞在这一步,无法进入后续循环。要实现两者同时运行,需要将Client的任务作为后台任务启动,而非同步等待其完成,同时要正确管理Scoped Service的生命周期。
修改后的Manager代码示例
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 存储所有启动的Client任务,用于后续管理与优雅关闭 var clientTasks = new List<Task>(); // 示例:启动一个Client(实际可根据业务逻辑在循环中按需启停) var scope = Services.CreateScope(); var client = scope.ServiceProvider.GetRequiredService<IClient>(); // 启动Client的后台任务,不阻塞Manager流程 var clientTask = Task.Run(async () => { try { await client.DoWork(stoppingToken); } finally { // Client任务结束后释放Scope,避免资源泄漏 scope.Dispose(); } }, stoppingToken); clientTasks.Add(clientTask); // Manager自身的循环逻辑 while (!stoppingToken.IsCancellationRequested) { await Task.Delay(1000, stoppingToken); // 这里可添加业务逻辑:比如按需启动新Client // if (需要启动新Client的条件) // { // var newScope = Services.CreateScope(); // var newClient = newScope.ServiceProvider.GetRequiredService<IClient>(); // var newClientTask = Task.Run(async () => // { // try { await newClient.DoWork(stoppingToken); } // finally { newScope.Dispose(); } // }, stoppingToken); // clientTasks.Add(newClientTask); // } // 清理已完成的任务,避免列表无限膨胀 clientTasks.RemoveAll(t => t.IsCompleted); } // 应用停止时,等待所有Client任务完成,保证优雅关闭 await Task.WhenAll(clientTasks); }
关键说明
- 非阻塞启动Client任务:用
Task.Run将Client的DoWork包装为后台任务,Manager无需等待其完成即可继续执行自身的循环逻辑。 - 正确管理Scoped生命周期:将Scope的释放放在Client任务的
finally块中,确保Client运行期间Scope始终有效,避免依赖注入的服务被提前回收。 - 任务管理与优雅关闭:通过
clientTasks列表跟踪所有启动的Client任务,在应用停止时调用Task.WhenAll等待所有Client任务结束,防止强制终止导致的资源泄漏。 - 支持单独控制Client生命周期:如果需要单独停止某个Client(而非依赖全局的停止令牌),可以为每个Client创建独立的
CancellationTokenSource,示例如下:
// 为单个Client创建独立的取消令牌(关联全局令牌,保证应用停止时也能触发取消) var clientCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); var clientTask = Task.Run(async () => { try { await client.DoWork(clientCts.Token); } finally { scope.Dispose(); clientCts.Dispose(); } }, stoppingToken); // 后续需要停止该Client时,调用: // clientCts.Cancel();
这样就能实现Manager与多个Client同时运行,且Client的生命周期可灵活控制,无需阻塞Manager的执行流程。
内容的提问来源于stack exchange,提问作者Brad R.
相关产品推荐
相关产品推荐

