Reputation: 2628
How is it possible to run multiple IHostedServices in parallel?
I use the WorkerService in .Net Core 3.0 and want both services to run parallel. Currently the second service is waiting for the first one to finish. Both services should run endlessly.
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<ServiceA>();
services.AddHostedService<ServiceB>();
});
}
A service looks like this:
public class ServiceA : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
Console.WriteLine("Sample");
await Task.Delay(5000, stoppingToken);
} while (!stoppingToken.IsCancellationRequested);
}
}
// edit:
Very reluctantly I would use a Task.Run(() => method());
method like this. But of course this way always works:
public class ServiceA : BackgroundService
{
public override Task StartAsync(CancellationToken cancellationToken)
{
Task.Factory.StartNew(() => ExecuteAsync(cancellationToken), cancellationToken);
return Task.CompletedTask;
}
}
Upvotes: 14
Views: 14165
Reputation: 91
I've had the same kind of issue: Multiple service that do different work at different frequencies.
When looking into it, BackgroundService
seems to be designed for sequential execution (an infinite loop based service's worst enemy).
After getting a hint from this thread, I found the solution that works for my case using Microsoft's Timer Service example.
The base TimerService
implements IHostedService
and IAsyncDisposable
:
StartAsync()
starts the timer on the DoWork()
DoWork()
is your overridable main work procedure.StopAsync()
stops the timer gracefully.DisposeAsync()
cleans up.I've tested by deriving multiple TimerService
s with different execution frequencies and adding them with services.AddHostedService<>();
.
They all start and run at the same time, do their bit on clock.
/!\ It is not Task
based as it uses timer events. Just pointing this out because I've already had quite a difficult troubleshooting experience the one time I mixed time-based events and Tasks /!\
Upvotes: 6
Reputation: 403
No need to manually create a task. The default StartAsync calls ExecuteAsync and returns that task to be awaited somewhere else.
So, you can do return base.StartAsync(cancellationToken)
before returning Task.Completed in StartAsync.
Upvotes: 0
Reputation: 1187
I asked myself a similar question and made some search but couldn't find a good answer.
I solved the issue running every background service in Task.Run
with a cancellation token from BackgroundService.ExecuteAsync()
I have 2 services like you.
public class BackgroundService1: BackgroundService
{
public BackgroundService1()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
//Second service is just like the first one:
public class BackgroundService2: BackgroundService
{
public BackgroundService2()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
and register them in Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<BackgroundService1>();
services.AddHostedService<BackgroundService2>();
})
.UseWindowsService()
Upvotes: 9