user10285265
user10285265

Reputation:

Blocking call in BackgroundService makes yet another BackgroundService not run

I have 2 background services.

Both of them have a call to a method that is NOT async. This method I cannot control - I can wrap it, sure.

I wire the backgroundservice(s) up by adding them to the servicecollection:

services.AddSingleton<IHostedService, BS1>();
services.AddSingleton<IHostedService, BS2>();

The execute async looks like this in each of them.

protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
     log.LogInformation($"Start consuming from topic: {eventStreamConsumer.Topic}");

     while (stoppingToken.IsCancellationRequested == false)
     {
           try
           {
                async...
                var consumeResult = eventStreamConsumer.Consume();

The Consume method is blocking the thread here since it's not async.

My approach so far has been to wrap the inner workings of execute async into a Task.Factory.Start, but if the Consume does not return, the thread still hangs.

I would like to truly run this on a separate thread, but bear in mind the execute async has dependencies on other instances in the class - don't know if the will be a problem?

How does this approach look like?

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var task = new Task(() =>
        {
            while (stoppingToken.IsCancellationRequested == false)
            {
                try
                {
                    var consumeResult = eventStreamConsumer.Consume(); 
....
                }
                catch (Exception e)
                {
                    //swallow
                }
            }
        }, stoppingToken, TaskCreationOptions.LongRunning);

        task.Start();

        return Task.FromResult<object>(null);
    }

Upvotes: 1

Views: 1482

Answers (1)

Luaan
Luaan

Reputation: 63772

The way async works is exactly that it returns a Task. That's the part you're missing: instead of waiting for the task to finish or reading its Result, return the task itself.

Also, don't use new Task followed by Task.Start. Task.Run is what you actually want pretty much every time (the only exception being if you're creating your own task scheduler).

Upvotes: 1

Related Questions