Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should .net core `IHostedService` start a new thread

.net core BackgroundService or IHostedService's start method is async:

//IHostedService
Task StartAsync(CancellationToken cancellationToken);
//BackgroundService
Task ExecuteAsync(CancellationToken stoppingToken);

So should I write all the logic in the ExecuteAsync/StartAsync method, or should I just start a new thread and return right away?

For example, which of the following two is the correct implementation?

1.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    new Thread(async () => await DoWork(stoppingToken)).Start();

    await Task.CompletedTask;
}

private async Task DoWork(CancellationToken stoppingToken) 
{
    while (!stoppingToken.IsCancellationRequested)
        //actual works
}

2.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        //actual works
        await Task.Delay(1000);//e.g
    }
}

Semantically I think the second one seems to be right, but if there're several IHostedServices, can they run in parallel with the second form?

Edit 1

I also write a sample program that illustrate the hosted services aren't themselves run as seperated threads.

The message "Waiting for signal.." won't be written console until I type a q:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace BackgroundTaskTest
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var host = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    IConfiguration config = hostContext.Configuration;

                    //register tasks
                    services.AddHostedService<ReadService>();
                    services.AddHostedService<BlockService>();
                })
                .UseConsoleLifetime()
                .Build();

            await host.RunAsync();
        }
    }

    public static class WaitClass
    {
        public static AutoResetEvent Event = new AutoResetEvent(false);
    }

    public class ReadService : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var c = Console.ReadKey();
                if (c.KeyChar == 'q')
                {
                    Console.WriteLine("\nTrigger event");
                    WaitClass.Event.Set();
                }
                await Task.Delay(1);
            }
        }
    }

    public class BlockService : BackgroundService
    {
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                Console.WriteLine("Waiting for signal..");
                WaitClass.Event.WaitOne();
                Console.WriteLine("Signal waited");
            }
            return Task.CompletedTask;
        }
    }
}
like image 329
mosakashaka Avatar asked Jun 10 '19 12:06

mosakashaka


People also ask

Is IHostedService a singleton?

When you register implementations of IHostedService using any of the AddHostedService extension methods - the service is registered as a singleton.

What is IHostedService in .NET core?

The IHostedService interface provides a convenient way to start background tasks in an ASP.NET Core web application (in . NET Core 2.0 and later versions) or in any process/host (starting in . NET Core 2.1 with IHost ).

What is background task in .NET core?

In ASP.NET Core, background tasks can be implemented as hosted services. A hosted service is a class with background task logic that implements the IHostedService interface. This article provides three hosted service examples: Background task that runs on a timer. Hosted service that activates a scoped service.

What is background service in C#?

CreateDefaultBuilder(args) . ConfigureWebHostDefaults(webBuilder => { webBuilder. UseStartup<Startup>(); }); } A background service is a specific type that runs asynchronously within a console application host with the idea it does not interfere with the primary process.


2 Answers

Just implement it using async/await, as your second example shows. There is no need for an extra Thread.

Side note: Thread should only be used for COM interop; there are far better solutions these days for every other former use case of Thread. As soon as you type new Thread, you already have legacy code.

like image 200
Stephen Cleary Avatar answered Oct 31 '22 12:10

Stephen Cleary


For StartAsync(), at least, you should start a separate thread. If your job is going to take a significant length of time to complete. Otherwise -- at least in my experience, with .NET Core 2.1 on Windows 10 -- the IHostedService won't register as "started" until all the work is done.

I have a test console app that HTTP-GETs the same URL 360 times, waiting 10 seconds between each, and counting how many times it succeeds and how many times it fails. In this app, I have configured ILogger logging with NLog, going both to a text file and to the console. Highest level of logging verbosity for each.

Once the hosted application finishes starting, I see a message logged to the console (and to the file), telling me that the hosted application has started, and that I can press Ctrl+C to quit.

If in StartAsync(), I simply await the test method that does all the work, then I don't see that message logged until all the work is done, over an hour later. If instead, I kick off a new thread as in your first example, then I see the Ctrl+C instructions almost immediately, as I should.

So empirically, your first example appears to be correct.

like image 44
Stephen G Tuggy Avatar answered Oct 31 '22 11:10

Stephen G Tuggy