cSteusloff
cSteusloff

Reputation: 2628

How to run multiple BackgroundService parallel in .net core 3.0?

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

Answers (3)

Cyrille Belfort
Cyrille Belfort

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 TimerServices 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

DBK
DBK

Reputation: 403

No need to manually create a task. The default StartAsync calls ExecuteAsync and returns that task to be awaited somewhere else.

https://github.com/aspnet/Hosting/blob/master/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs#L30

So, you can do return base.StartAsync(cancellationToken) before returning Task.Completed in StartAsync.

Upvotes: 0

ibrahimozgon
ibrahimozgon

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

Related Questions