C#怎么配置后台任务 BackgroundService使用方法

BackgroundService 是 ASP.NET Core 中实现长运行托管后台任务的标准方式,需继承并重写 ExecuteAsync,使用 CancellationToken 响应关闭,通过 AddHostedService 注册,支持 DI 但需注意作用域生命周期。

在 C#(特别是 .NET 5+ 的 ASP.NET Core 应用中),BackgroundService 是实现**长运行、托管后台任务**的标准方式。它由 IServiceCollection 托管,随应用启动/停止自动生命周期管理,比裸写线程或 Task.Run 更安全、更可控。

1. 继承 BackgroundService 并重写 ExecuteAsync

核心是继承 BackgroundService,并重写 ExecuteAsync(CancellationToken stoppingToken) 方法。该方法会被框架调用一次,你需在此内部实现持续运行的逻辑(通常用循环 + 延迟)。

  • 必须使用 stoppingToken 检查取消请求,确保能响应应用关闭
  • 避免直接用 while(true) 死循环,要用 await Task.Delay(..., stoppingToken) 支持中断
  • 异常未捕获会导致后台服务意外终止(不会自动重启)

示例:每 5 秒打印一次时间

public class ClockService : BackgroundService
{
    private readonly ILogger _logger;

    public ClockService(ILogger logger) => _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("当前时间: {Time}", DateTime.Now);
            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
    }
}

2. 在 Program.cs 中注册为托管服务

.NET 6+ 推荐在 Program.cs 的服务容器中注册,使用 AddHostedService()AddScoped().AddHostedService(x => x.GetRequiredService())(推荐前者)。

  • 注册后,框架会在应用启动时调用 StartAsync,关闭时调用 StopAsync
  • 服务默认是单例生命周期,无需手动管理实例

注册代码(Program.cs):

var builder = WebApplication.CreateBuilder(args);

// 注册后台服务(自动托管)
builder.Services.AddHostedService();

// 其他服务...
builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();
app.Run();

3. 使用依赖注入(DI)获取其他服务

BackgroundService 支持构造函数注入,可安全使用 ILoggerIConfiguration、数据库上下文(注意:若用 EF Core,请用 IServiceScopeFactory 创建作用域,避免跨作用域共享 DbContext)。

  • 不要在后台服务中长期持有 scoped 服务(如 DbContext),应按需创建新 scope
  • 需要访问数据库时,建议通过 IServiceScopeFactory 创建临时 scope

带数据库操作的片段示例:

public class DataSyncService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly ILogger _logger;

    public DataSyncService(IServiceScopeFactory scopeFactory, ILogger logger)
    {
        _scopeFactory = scopeFactory;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var scope = _scopeFactory.CreateScope();
            var context = scope.ServiceProvider.GetRequiredService();

            // 执行查询或保存...
            var count = await context.Users.CountAsync(stoppingToken);
            _logger.LogInformation("用户总数: {Count}", count);

            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
}

4. 启动与停止的注意事项

BackgroundServiceStartAsyncStopAsync 是可重写的,但多数场景无需干预。需注意:

  • StopAsync 有默认 5 秒超时(可通过 IHostOptions.ShutdownTimeout 修改)
  • 若你的清理逻辑耗时较长,应在 stoppingToken 触发后尽快退出循环,并在 StopAsync 中做收尾(如释放资源、提交事务)
  • 不建议在 StopAsync 中执行阻塞 IO 或长时间等待 —— 应配合 stoppingToken 提前退出

基本上就这些。用好 BackgroundService 关键就三点:正确响应取消令牌、合理使用 DI、避免在后台任务里滥用生命周期不匹配的服务。它不是万能定时器(复杂调度建议用 Quartz.NET 或 Hangfire),但对轻量级周期性任务非常够用。