How to implement parallel long running background tasks in .NET Core 3? - c#

I have .NET Core console application containing two independent tasks that should be running in parallel for the entire life-time of the application. I was thinking to use BackgroundService:
class BackgroundTaskOne : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// do long running task for the entire life-time of application
while(true)
{
// do work one
}
}
catch (Exception e)
{
// log
}
}
return Task.CompletedTask;
}
}
class BackgroundTaskTwo : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// do long running task for the entire life-time of application
while(true)
{
// do work two
}
}
catch (Exception e)
{
// log
}
}
return Task.CompletedTask;
}
}
And register them like this:
services.AddHostedService<BackgroundTaskOne>();
services.AddHostedService<BackgroundTaskTwo>();
But these are going to run in order. So I have two questions:
Is there a way to make these two run in parallel?
Are there any other alternatives to run two long-running background processes in .NET Core in parallel?

The docs of BackgroundService.ExecuteAsync say
The implementation should return a task that represents the lifetime of the long running operation(s) being performed.
Your implementation returns a completed task when the whole work is done. In fact you implemented it to run sync and not async and that is the reason for not running parallel.
Here is a sample implementation with some fake async work:
class BackgroundTaskOne : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// do work one
await Task.Delay( 100 );
}
catch (Exception e)
{
// log
}
}
}
}

As documentation says:
https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/fundamentals/host/hosted-services.md
No further services are started until ExecuteAsync becomes
asynchronous, such as by calling await.
As long as you do not have async clause mentioned at the ExecuteAsync method above, I suspect your method is synchronous at whole. This is why two services are called sequentially, not in parallel. Give them a break, introduce good amount of awaitable code.

Related

ASP.Net Core Background Service with Task Queue, does it need a Task.Delay?

I have followed the Microsoft documentation on how to implement a BackgroundService with a task queue, but I noticed there's no Task.Delay in the main loop, is it a concern or will it still run fine?
This is the service class in question:
public class BackgroundTasksService : BackgroundService
{
public IBackgroundTaskQueue TaskQueue { get; }
public BackgroundTasksService(IBackgroundTaskQueue taskQueue)
{
TaskQueue = taskQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem =
await TaskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
await base.StopAsync(stoppingToken);
}
}
This code was taken from
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio#queued-background-tasks
The answer lies in what is happening if this code executes while there is nothing in the queue.
var workItem = await TaskQueue.DequeueAsync(stoppingToken);
The answer is that nothing is happening. This line of code doesn't execute over and over again until something is dequeued. That's why there's no need for a delay.
It also doesn't block until something is dequeued.
What happens is that the thread executing the method is freed to do something else. No thread is dedicated to the execution of DequeueAsync. (See this excellent post - There Is No Thread.)
When and if an item appears in the queue to be dequeued, then an available thread is assigned to resume execution of the method.
DequeueAsync only throws an exception if
the cancellation token is canceled
the queue's Complete method is called and the queue is empty, which means that you're no longer expecting anything to appear in the queue.

Background tasks are being queued and not executed

I've implemented the BackgroundQueue as explained here, and as shown:
public ActionResult SomeAction()
{
backgroundQueue.QueueBackgroundWorkItem(async ct =>
{
//Do some work...
});
return Ok();
}
I registered the BackgroundQueue with Autofac as:
builder.RegisterType<BackgroundQueue>()
.As<IBackgroundQueue>()
.SingleInstance();
So far so good. I call my controller action and the task is added to the queue. And there it stays without being executed.
So how do I get the task to execute?
The BackgroundQueue implementation that you took from the documentation is only one part to the solution: The background queue will just keep track of the jobs that you want to be executed.
What you will also need is right below that in the docs: The QueuedHostedService. This is a background service that gets registered with the DI container and is started when the application starts. From then on, it will monitor your BackgroundQueue and work off jobs as they get queued.
A simplified example implementation of this background service, without logging or error handling, could look like this:
public class QueuedHostedService : BackgroundService
{
private readonly IBackgroundQueue _backgroundQueue;
public QueuedHostedService(IBackgroundQueue backgroundQueue)
{
_backgroundQueue = backgroundQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _backgroundQueue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}

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;
}
}
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()
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 /!\
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.

Running a Task in the background (PCL)

I have a class with an async method:
public static async Task GetData() { ... }
In the app framework I am using I need to start that process and forget about it when the app starts:
protected override void OnStart()
{
await MyService.GetData();
}
I can't make OnStart async. How do I start it in a background task and forget about it?
I can't make OnStart Async. How do I start it in a background task and
forget about it?
Why not? Nothing prevents you from making it async. The async modifier doesn't affect the CLR method signature, i.e., you can override a void method and make it async:
abstract class AppBase
{
protected abstract void OnStart();
}
class App: AppBase
{
public static async Task GetData() { await Task.Delay(1); }
protected override async void OnStart()
{
await GetData();
}
}
This way, at least you'll see an exception if GetData throws, unlike what the other answer suggests.
Make sure you understand how async void methods and Task error handling work in general, this material may be helpful.
Some other problems with Task.Run( () => MyService.GetData() ):
as GetData is already asynchronous, there's very little sense in wrapping it with Task.Run. It's usually only done in a client-side UI app and only if GetData has a long-running synchronous part (before it hits its 1st await). Otherwise, you might as well just call GetData() without Task.Run and without await (which also would be a bad idea: in either case, you'd be doing a fire-and-forget call without observing possible exceptions).
Task.Run will start GetData on a random pool thread without synchronization content, which may be a problem for either a UI app or an ASP.NET app.
If you want to fire this async operation and forget about it all you need to do is invoke the method without awaiting the returned task:
protected override void OnStart()
{
MyService.GetDataAsync();
}
However, since you're not observing the task you would never know if it completed successfully.
You should either keep a reference to the task and await it in a later time:
public Task _dataTask;
protected override void OnStart()
{
_dataTask = MyService.GetDataAsync();
}
public Task AwaitInitializationAsync()
{
return _dataTask;
}
Or add a continuation handling any exceptions:
protected override void OnStart()
{
MyService.GetDataAsync().ContinueWith(t =>
{
try
{
t.Wait();
}
catch (Exception e)
{
// handle exceptions
}
});
}
You shouldn't use Task.Run as Noseratio explained, however using async void is much worse since an exception in an async void method (which isn't a UI event handler) would tear down the entire process*.
You can try to make the method async void while making sure there won't be any exceptions thrown inside it with a try-catch block:
protected override async void OnStart()
{
try
{
await GetData();
}
catch (Exception e)
{
// handle e.
}
}
But I would still recommend against it since even the chance of a complete crash is dangerous.
*You can get around that by registering an even handler for AppDomain.CurrentDomain.UnhandledException but this should be a last resort, not a best practice

Waiting on a named semaphore with WaitOne(100) vs WaitOne(0) + Task.Delay(100)

I need to access a resource in a Windows 8.1 App shared by two processes: the app itself and a Background Task, so I need a named Semaphore, SemaphoreSlim does not apply there and as I do async work between acquisition and release I cannot use a Mutex.
I have created a class in a PCL that creates the Semaphore and allows me to await the WaitOne method this way:
public sealed class AsyncSemaphore:IDisposable
{
Semaphore _semaphore;
public AsyncSemaphore(int initialCount, int maximumCount, string name)
{
_semaphore = new Semaphore(initialCount, maximumCount, name);
}
public IAsyncOperation<bool> WaitOneAsync()
{
return AsyncInfo.Run<bool>(cancellationToken =>
Task.Run(()=>{
while (!_semaphore.WaitOne(100))
{
Logger.Log("Waiting...");
cancellationToken.ThrowIfCancellationRequested();
}
return true;
},cancellationToken));
}
public int Release()
{
return _semaphore.Release();
}
public void Dispose()
{
if (_semaphore != null)
{
_semaphore.Dispose();
_semaphore = null;
}
}
}
But WaitOneAsync can be written also like this:
public IAsyncOperation<bool> WaitOneAsync()
{
return AsyncInfo.Run<bool>(async cancellationToken =>
{
while (!_semaphore.WaitOne(0))
{
Logger.Log("Waiting...");
await Task.Delay(100, cancellationToken);
}
return true;
});
}
Then I use it in my code like this:
_semaphore= new AsyncSemaphore(1,1,"uniquename");
//....
await _semaphore.WaitOneAsync();
try
{
//do more async work
}
finally
{
_semaphore.Release();
}
Is this correct? Which one is best and uses less resources?
The first option holds a thread throughout the entire wait, first by synchronously waiting and then by busy waiting (the while loop).
The second option is at least somewhat asynchronous as it uses Task.Delay to wait and only then resorts to busy waiting.
The second (async) option uses less resources, but needs to wait out the entire timeout (100ms) before checking again while the first (sync) can enter the semaphore immediately when it's released.
The async option uses less resources than the sync version but the actual synchronization is slower than in the sync version. So it comes down to what are your specific needs, scalability or speed.
You can optimize by lowering the timeout from 100ms and so bring the async option closer and closer to the sync version.

Categories