[英]Multiple AddHostedService dotnet core
When i tried to register more that one AddHostedService the method StartAsync is invoke only on the first当我尝试注册多个AddHostedService时,仅在第一个调用StartAsync方法
services.AddHostedService<HostServiceBox>(); // StartAsync is called
services.AddHostedService<HostServiceWebSocket>(); // DO NOT WORK StartAsync not called
services.AddHostedService<HostServiceLogging>(); // DO NOT WORK StartAsync not called
any idea?任何想法?
Thanks for the helps感谢您的帮助
Below a code who work I get around the problem by creating a helper在工作的代码下方,我通过创建帮助程序来解决问题
@statup.cs @statup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtBearerConfiguration(services);
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin()
.AllowCredentials();
}));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;
services.AddSignalR();
services.AddHostedService<HostServiceHelper>(); // <===== StartAsync is called
}
@HostServiceHelper.cs @HostServiceHelper.cs
public class HostServiceHelper : IHostedService
{
private static IHubContext<EngineHub> _hubContext;
public HostServiceHelper(IHubContext<EngineHub> hubContext)
{
_hubContext = hubContext;
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
Task.Run(() => ServiceWebSocket(), cancellationToken);
Task.Run(() => ServiceBox(), cancellationToken);
Task.Run(() => ServiceLogging(), cancellationToken);
}, cancellationToken);
}
public void ServiceLogging()
{
// your own CODE
}
public void ServiceWebSocket()
{
// your own CODE
}
public void ServiceBox()
{
// your own CODE
}
public Task StopAsync(CancellationToken cancellationToken)
{
//Your logical
throw new NotImplementedException();
}
}
A hosted service is usually a single task so I'd do it with a singleton.托管服务通常是一个单一的任务,所以我会用一个单例来完成。
// Hosted Services
services.AddSingleton<IHostedService, HttpGetCurrencyPairRequestSyncingService>();
services.AddSingleton<IHostedService, HttpPostCurrencyPairRequestSyncingService>();
And when in my class,在我上课的时候,
public class CurrencyPairCacheManagementService : BaseHostedService<CurrencyPairCacheManagementService>
, ICurrencyPairCacheManagementService, IHostedService, IDisposable
{
private ICurrencyPairService _currencyPairService;
private IConnectionMultiplexer _connectionMultiplexer;
public CurrencyPairCacheManagementService(IConnectionMultiplexer connectionMultiplexer,
IServiceProvider serviceProvider) : base(serviceProvider)
{
_currencyPairService = serviceProvider.GetService<CurrencyPairService>();
_connectionMultiplexer = connectionMultiplexer;
InitializeCache(serviceProvider);
}
/// <summary>
/// Operation Procedure for CurrencyPair Cache Management.
///
/// Updates every 5 seconds.
///
/// Objectives:
/// 1. Pull the latest currency pair dataset from cold storage (DB)
/// 2. Cross reference checking (MemoryCache vs Cold Storage)
/// 3. Update Currency pairs
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("CurrencyPairCacheManagementService is starting.");
stoppingToken.Register(() => _logger.LogInformation("CurrencyPairCacheManagementService is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
var currencyPairs = _currencyPairService.GetAllActive();
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
_logger.LogWarning("CurrencyPairCacheManagementService background task is stopping.");
}
public void InitializeCache(IServiceProvider serviceProvider)
{
var currencyPairs = _currencyPairService.GetAllActive();
// Load them individually to the cache.
// This way, we won't have to update the entire collection if we were to remove, update or add one.
foreach (var cPair in currencyPairs)
{
// Naming convention => PREFIX + CURRENCYPAIRID
// Set the object into the cache
}
}
public Task InproPair(CurrencyPair currencyPair)
{
throw new NotImplementedException();
}
}
ExecuteAsync gets hit first, before carrying on with what you want it to do. ExecuteAsync 在继续执行您希望它执行的操作之前首先被命中。 You might also want to remove the generics declaration I have because my Base class runs with generics (If you don't run your hosted service base class with generics then I don't think you'll need to inherit IHostedService and IDisposable explicitly).您可能还想删除我的泛型声明,因为我的基类使用泛型运行(如果您不使用泛型运行托管服务基类,那么我认为您不需要显式继承 IHostedService 和 IDisposable)。
OK, this is 2022 and .NET 6 is out.好的,这是 2022 年,.NET 6 已经出局。 Now days it is not a problem to run multiple hosted services, as long as they are represented by different classes.现在运行多个托管服务不是问题,只要它们由不同的类表示。 Just like this:像这样:
public class Program
{
public static void Main(string[] args)
{
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.AddHostedService<Worker2>();
}).Build();
host.Run();
}
}
Both workers will run.两个工人都会跑。
But what if we need multiple instances of the same service class to run parallel?但是,如果我们需要同一服务 class 的多个实例来并行运行呢? That seems to be still impossible.这似乎仍然是不可能的。
I ended up implementing my own utility function to start multiple tasks in parallel and collect all the exceptions properly.我最终实现了自己的实用程序 function 以并行启动多个任务并正确收集所有异常。 Here:这里:
/// <summary>
/// Runs multiple cancelable tasks in parallel. If any of the tasks terminates, all others are cancelled.
/// </summary>
public static class TaskBunchRunner
{
public class BunchException : Exception
{
public AggregateException Agg { get; }
public BunchException(AggregateException agg) : base("Task bunch failed", agg)
{
Agg = agg;
}
public override string Message => $"Task bunch failed: {Agg.Message}";
public override string ToString() => $"BunchException -> {Agg.ToString()}";
}
public static async Task Bunch(this IEnumerable<Func<CancellationToken, Task>> taskFns, CancellationToken ct)
{
CancellationTokenSource combinedTcs = CancellationTokenSource.CreateLinkedTokenSource(ct);
CancellationToken ct1 = combinedTcs.Token;
Task[] tasks = taskFns.Select(taskFn => Task.Run(() => taskFn(ct1), ct1)).ToArray();
// If any of the tasks terminated, it may be because of an error or a cancellation.
// In both cases we cancel all of them.
await Task.WhenAny(tasks); // this await will never throw
combinedTcs.Cancel();
var allTask = Task.WhenAll(tasks); // this will collect exceptions in an AggregateException
try
{
await allTask;
}
catch (Exception)
{
if (allTask.Exception != null) throw new BunchException(allTask.Exception);
throw;
}
// Why not just await Task.WhenAll() and let it throw whatever it is?
// Because await will unwrap the aggregated exception and rethrow just one of the inner exceptions,
// losing the information about others. We want all the exceptions to be logged, that is why
// we get the aggregated exception from the task. We also throw it wrapped into a custom exception, so the
// outer await (in the caller's scope) does not unwrap it again. :facepalm:
}
}
Now we create a single hosted service and make its ExecuteAsync
method run several tasks as a bunch:现在我们创建一个托管服务并使其ExecuteAsync
方法作为一组运行多个任务:
class MySingleService
{
private readonly string _id;
public MySingleService(string id){ _id = id; }
public async Task RunAsync(CancellationToken ct)
{
await Task.Delay(500, ct);
Console.WriteLine($"Message from service {_id}");
await Task.Delay(500, ct);
}
}
class MyHostedService: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
MySingleService[] individuals = new[]
{
new MySingleService("1"),
new MySingleService("2"),
new MySingleService("3"),
};
await individuals
.Select<MySingleService, Func<CancellationToken, Task>>(s => s.RunAsync)
.Bunch(stoppingToken);
}
}
public class Program
{
public static void Main(string[] args)
{
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyHostedService>();
}).Build();
host.Run();
}
}
Note: the TaskBunchRunner
class was taken from a real project and proven to work, while the usage example is made up and not tested.注意: TaskBunchRunner
class 取自一个真实项目并被证明可以工作,而使用示例是编造的,未经测试。
I know it is not exactly what the OP asked for.我知道这不是 OP 所要求的。 But may be useful for someone who ends up here having the same problem as I had.但是对于最终在这里遇到与我相同的问题的人可能很有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.