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

.NET Core如何用同一类带不同参数注册多个HostedService

.NET Core托管服务多实例注册问题解决

问题描述

需要将MyBackgroundService类注册为3个独立的HostedService实例,每个实例传入1-3的不同整数参数,但实际运行时断点仅触发一次,无法实现多实例并行运行。

问题根源

  1. 闭包变量捕获问题:循环中的变量i被闭包捕获的是引用而非值,导致三个实例最终拿到的都是循环结束后的i=3,逻辑上无法区分实例,看起来像只运行了一次。
  2. 作用域管理不当:在服务注册的工厂方法中创建作用域并持有,会导致Scoped生命周期的服务(如MyDBContext)被长期持有,违背Scoped服务的设计意图,同时可能引发资源占用问题。
  3. 同步阻塞问题:ExecuteAsync方法中没有异步等待逻辑,会同步占用线程,导致托管服务运行异常。

解决方案及修改代码

1. 修复服务注册逻辑

修改循环内的变量捕获方式,并调整作用域创建时机:

var builder = Host.CreateDefaultBuilder(args);

string env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
string version = typeof(Program).Assembly.GetName().Version.ToString();
var config = new ConfigurationBuilder()
    .AddJsonFile($"appsettings.{env}.json", optional: false, reloadOnChange: true).Build();

var v = config.GetConnectionString("yshDb");

String logpath = Path.Join(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "TestServices.csv");

var csvLoggerProvider = new CsvLoggerProvider<MyBackgroundService>(logpath);

IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);

hostBuilder.ConfigureServices((hostContext, services) =>
{
    services.AddSingleton<ILoggerProvider>(csvLoggerProvider);
    services.AddDbContext<MyDBContext>(options =>
        options.UseSqlServer(hostContext.Configuration.GetConnectionString("connStr")), ServiceLifetime.Scoped);

    services.AddScoped<IDocumentMoveData, DocumentMoveData>();
    
    // 修复闭包变量问题:每次循环捕获当前i的值
    for (int i = 1; i <= 3; ++i)
    {
        int currentInstanceNumber = i;
        services.AddHostedService<MyBackgroundService>(s =>
        {
            // 这里只注入IServiceProvider,作用域在ExecuteAsync内部创建
            return new MyBackgroundService(
                s.GetRequiredService<IServiceProvider>(),
                currentInstanceNumber,
                s.GetRequiredService<ILogger<MyBackgroundService>>());
        });
    }
});

IHost host = hostBuilder.Build();

await host.StartAsync();
Console.ReadLine();

2. 修改MyBackgroundService实现

调整构造函数注入内容,在ExecuteAsync内部创建作用域并使用Scoped服务:

public class MyBackgroundService : BackgroundService
{
    private readonly ILogger<MyBackgroundService> _logger;
    private readonly IServiceProvider _serviceProvider;
    private readonly int _instanceNumber;

    // 注入IServiceProvider,用于后续创建作用域
    public MyBackgroundService(IServiceProvider serviceProvider, int instanceNumber, ILogger<MyBackgroundService> logger)
    {
        _serviceProvider = serviceProvider;
        _instanceNumber = instanceNumber;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Background service instance {InstanceNumber} started", _instanceNumber);
        
        while (!stoppingToken.IsCancellationRequested)
        {
            // 每次循环创建独立作用域,使用Scoped服务
            using (var scope = _serviceProvider.CreateScope())
            {
                var myService = scope.ServiceProvider.GetRequiredService<IDocumentMoveData>();
                var dbContext = scope.ServiceProvider.GetRequiredService<MyDBContext>();
                
                List<DocumentInfo> files = myService.TemplateFiles(_instanceNumber);
                // 这里可以添加业务逻辑处理,比如处理files
                
                _logger.LogInformation("Instance {InstanceNumber} processed {Count} files", _instanceNumber, files?.Count ?? 0);
            }
            
            // 添加异步延迟,避免同步阻塞线程
            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
        
        _logger.LogInformation("Background service instance {InstanceNumber} stopped", _instanceNumber);
    }
}

关键修改说明

  • 闭包变量修复:通过int currentInstanceNumber = i;在循环内部捕获当前循环的i值,确保每个服务实例拿到独立的参数。
  • 作用域正确管理:将作用域创建移到ExecuteAsync的循环内部,每次执行业务逻辑都使用新的Scoped服务实例,避免长期持有导致的资源问题。
  • 异步逻辑完善:添加await Task.Delay确保方法异步执行,不会阻塞托管服务的线程池。

内容的提问来源于stack exchange,提问作者Michael EstrinOne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:45:00