同一IHostedService实例的StartAsync被调用两次问题求助
问题现象
注册多个IHostedService实例时,第一个实例的StartAsync方法被调用两次,第二个实例的该方法未触发,但两个实例的构造函数均已执行。
相关代码
Program.cs
services.AddSingleton<IHostedService, ProductService>(provider => (ProductService)provider.GetService<IProductService>()!); services.AddSingleton<IProductService, ProductService>(); services.AddSingleton<IHostedService, ProductService>(provider => (ProductService)provider.GetService<IProductService>()!); services.AddSingleton<IProductService, ProductService>();
ProductService.cs
public class ProductService : IProductService, IHostedService { public async Task StartAsync(CancellationToken cancellationToken) { } public async Task StopAsync(CancellationToken cancellationToken) { } }
问题原因
当前注册逻辑存在核心问题:
IProductService被注册为单例(Singleton),因此两次调用provider.GetService<IProductService>()拿到的是同一个实例。- 两次注册
IHostedService时,都指向了这个唯一的ProductService实例,相当于把同一个对象重复注册为IHostedService。宿主启动时会遍历所有IHostedService条目调用StartAsync,导致同一个实例的方法被执行两次,而你期望的第二个独立实例根本没有被创建。
解决方案
要创建多个独立的ProductService实例,需确保每个IHostedService注册对应不同的实例,以下是几种可行实现方式:
方式一:工厂函数直接生成新实例
如果ProductService无需依赖注入,或可直接构造,每次注册IHostedService时返回新实例:
// 注册第一个独立实例 services.AddSingleton<IHostedService>(_ => new ProductService()); // 注册第二个独立实例 services.AddSingleton<IHostedService>(_ => new ProductService());
方式二:手动绑定实例到双服务类型
如果需要同时将每个ProductService实例注册为IProductService供其他服务使用,可手动创建实例并分别注册:
// 创建第一个实例并注册 var service1 = new ProductService(); services.AddSingleton<IProductService>(service1); services.AddSingleton<IHostedService>(service1); // 创建第二个实例并注册 var service2 = new ProductService(); services.AddSingleton<IProductService>(service2); services.AddSingleton<IHostedService>(service2);
注意:直接解析
IProductService时会拿到最后注册的实例(service2),若需区分不同实例,建议使用命名服务。
方式三:命名服务区分实例(推荐)
通过AddKeyedSingleton注册命名的IProductService,再绑定到对应的IHostedService,确保每个实例独立且可精准获取:
// 注册第一个命名实例 services.AddKeyedSingleton<IProductService, ProductService>("product-service-1"); services.AddSingleton<IHostedService>(provider => provider.GetRequiredKeyedService<IProductService>("product-service-1")); // 注册第二个命名实例 services.AddKeyedSingleton<IProductService, ProductService>("product-service-2"); services.AddSingleton<IHostedService>(provider => provider.GetRequiredKeyedService<IProductService>("product-service-2"));
其他服务需要使用特定实例时,可通过[FromKeyedServices("product-service-1")]特性注入对应的IProductService。
内容的提问来源于stack exchange,提问作者Starfish
相关产品推荐
相关产品推荐

