Search code examples
c#async-await.net-6.0workerbackground-service

Is it ok to use task.Run to parallel two backgroundservice in a .NET 6 Worker process?


I'm working with Net6 and Worker process (not asp.net). I need to have two or more backgroundservices running in parallel in the same worker process.

I do that with Task.Run(()=>...) but I don't know if it is a good practice and I would ask to you what do you think about it.

So I create a new Worker project with two hostedServices:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            services
            .AddHostedService<Worker>()
            .AddHostedService<Worker2>();
        });

Backgroundservice Worker implementation:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

    for (int i = 0; i < 1000; i++)
    {
        if (stoppingToken.IsCancellationRequested) break;
        Console.WriteLine($"Worker1 - {i}");
    }
}

Worker2:

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

        for (int i = 0; i < 1000; i++)
        {
            if (stoppingToken.IsCancellationRequested) break;
            Console.WriteLine($"Worker2 - {i}");
        }
    }

I'll got as result

Worker1 - 1
Worker1 - 2
...
Worker1 - 999
Worker2 - 1
Worker2 - 2
...
Worker2 - 999

So the task Worker2 is executed after the Worker.

If I use Task.Run inside each ExecuteAsync:

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Run(() =>
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                for (int i = 0; i < 1000; i++)
                {
                    if (stoppingToken.IsCancellationRequested) break;
                    Console.WriteLine($"Worker1 - {i}");
                }

            });
        }

and

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Run(() =>
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

            for (int i = 0; i < 1000; i++)
            {
                if (stoppingToken.IsCancellationRequested) break;
                Console.WriteLine($"Worker1 - {i}");
            }

        });
    }

The output is

Worker1 - 1
Worker2 - 1
Worker2 - 2
Worker1 - 1
...

Which to me is good but I'm asking to you if it is a good practice to use Task.Run to parallelize the two backgroundservices.

Somewhere I read something about it is a good practice avoid Task.Run. What else should I use then?

Thank you.


Solution

  • I do that with Task.Run(()=>...) but I don't know if it is a good practice and I would ask to you what do you think about it.

    Using Task.Run is normal because ExecuteAsync will otherwise block the host startup, as I describe on my blog.

    They are planning to fix this behavior in .NET 7.0, probably by just having BackgroundService run ExecuteAsync within a Task.Run so everyone else doesn't have to do it.