讲师中心 微信公众号
AI工具推荐 视频效率加速

c# BackgroundService 和 IHostedService 的优雅关闭(Graceful Shutdown)

小静姑娘_1545

小静姑娘_1545

发布时间:2026-02-01 10:15:10

|

754人浏览过

|

来源于php中文网

原创

BackgroundService.StopAsync 中的 CancellationToken 是主机注入的关机超时令牌(默认5秒),非用户传入的令牌;需用 WaitAsync(cancellationToken) 等待任务并响应取消,避免卡住关机。

c# backgroundservice 和 ihostedservice 的优雅关闭(graceful shutdown)

BackgroundService.StopAsync 里的 cancellation token 是关键

它不是你传进来的那个 cancellationToken,而是框架在调用 StopAsync 时注入的「关机超时令牌」。这个令牌会在主机关闭超时后被触发(默认 5 秒),和你在 StartAsync 中启动的长期任务所监听的 CancellationToken 不是同一个。

常见错误是直接在 StopAsync 里 await 长期任务而不做超时控制,导致关机卡住或被强制终止:

public override async Task StopAsync(CancellationToken cancellationToken)
{
    // ❌ 错误:await 一个没受 cancellationToken 约束的任务,可能永远不返回
    await _processingTask; // 如果 _processingTask 内部没响应取消,这里就挂住

    // ✅ 正确:用传入的 cancellationToken 做超时等待,并确保内部任务可取消
    await _processingTask.WaitAsync(cancellationToken); // .WaitAsync 是 Task 的扩展方法(需引用 Microsoft.Extensions.Tasks)
}

注意:WaitAsync 是 Task 的扩展方法,来自 Microsoft.Extensions.Tasks 包,.NET 6+ 已内置;若用旧版需手动安装。

IHostedService 实现必须自己处理取消逻辑

BackgroundService 是 IHostedService 的封装,自动帮你管理生命周期和取消信号;但如果你直接实现 IHostedService,就得手动把 StartAsync 和 StopAsync 的 CancellationToken 透传给所有异步工作流。

典型疏漏点:

  • 在 StartAsync 启动 Task.Run 或 Task.Factory.StartNew,却没把 cancellationToken 传进去
  • 用 while (true) 轮询,但没在循环开头检查 cancellationToken.IsCancellationRequested
  • 调用第三方异步方法(如 HttpClient.SendAsync)时,忘了把 cancellationToken 传过去

示例中漏掉取消传播的写法:

public Task StartAsync(CancellationToken cancellationToken)
{
    _workerTask = Task.Run(() =>
    {
        while (true)
        {
            DoWork();
            Thread.Sleep(1000); // ❌ Sleep 不响应 cancellation
        }
    });
    return Task.CompletedTask;
}

应改为:

public Task StartAsync(CancellationToken cancellationToken)
{
    _workerTask = ExecuteLoopAsync(cancellationToken);
    return Task.CompletedTask;
}

private async Task ExecuteLoopAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        await DoWorkAsync(cancellationToken); // ✅ 所有异步操作都接收并传递 cancellationToken
        await Task.Delay(1000, cancellationToken); // ✅ Delay 支持取消
    }
}

注册时别用 AddSingleton<IHostedService>

虽然编译通过,但这是危险操作:IHostedService 实例由主机在启动/停止时统一调度,如果注册为 Singleton,且该服务又依赖了 Scoped 或 Transient 服务(比如 DbContext、ILogger<T>),就会引发对象生命周期冲突——最常见的是 ObjectDisposedException 或日志丢失。

outlook-microsoft
outlook-microsoft

微软 Outlook 邮箱和日历管理工具(支持世纪互联版 Office 365)。用于查看、发送、搜索邮件,管理日历日程,创建会议,查询忙闲状态。注意:此功能仅支持世纪互联(21Vianet)版 Microsoft 365,使用 login.chinacloudapi.cn。

下载

正确注册方式只有这一种:

services.AddHostedService<MyBackgroundService>();

它等价于:

services.AddSingleton<IHostedService, MyBackgroundService>(); // ❌ 表面一样,但 AddHostedService 内部做了额外校验和包装

但更安全的做法是显式使用 AddHostedService,它会自动处理作用域上下文,在 StartAsync 中正确解析 Scoped 服务(通过 IServiceScopeFactory 创建新 scope)。

ShutdownTimeout 影响 StopAsync 的执行窗口

主机默认只给 StopAsync 5 秒时间完成清理。如果业务需要更久(比如要刷完缓存、发完最后一批消息),必须显式配置:

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<MyBackgroundService>();
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    })
    .UseShutdownTimeout(TimeSpan.FromSeconds(30)); // ✅ 设为 30 秒

这个设置影响所有 IHostedService 的 StopAsync 总体超时,不是单个服务的专属时间。所以多个后台服务共用这 30 秒,谁耗得久,谁就容易被截断。

另外,Kestrel 在收到 SIGTERM 后也会启动自己的 shutdown 流程,和 IHostedService 并行;如果 HTTP 请求还在处理中,它们不会被立即中断,但新请求会被拒绝。这意味着你的 StopAsync 里不该再发起新的 HTTP 调用(除非明确带超时且容忍失败)。

真正容易被忽略的是:StopAsync 抛出异常会导致整个主机关机失败(表现为进程不退出、日志无后续),而这个异常往往被吞掉或只出现在 debug 输出里。务必在 StopAsync 外层加 try/catch 并记录日志。

热门AI工具

更多
Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

354

2023.09.25

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

13926

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

1645

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

3817

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

5981

2024.03.01

http500解决方法
http500解决方法

http500解决方法有检查服务器日志、检查代码错误、检查服务器配置、检查文件和目录权限、检查资源不足、更新软件版本、重启服务器或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3563

2023.11.09

http请求415错误怎么解决
http请求415错误怎么解决

解决方法:1、检查请求头中的Content-Type;2、检查请求体中的数据格式;3、使用适当的编码格式;4、使用适当的请求方法;5、检查服务器端的支持情况。更多http请求415错误怎么解决的相关内容,可以阅读下面的文章。

894

2023.11.14

HTTP 503错误解决方法
HTTP 503错误解决方法

HTTP 503错误表示服务器暂时无法处理请求。想了解更多http错误代码的相关内容,可以阅读本专题下面的文章。

6546

2024.03.12

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

0

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn