I am doing an ASP.NET Web API and have a BackgroundService like this:
Inside Doing, I to await a task 1:
The problem is with the TimeSpan.FromSeconds(0.5)) the ExecuteAsync will do create a new Doing() without waiting for my task to be done.
The console result :
How can I resolve this? Or is there a way to achieve a background task with await for the task completion?
Don't use a Timer.
Instead set up a loop and use Task.Delay for the wait period.
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
var delay = TimeSpan.FromSeconds(0.5);
while (!cancellationToken.IsCancellationRequested)
{
await Doing();
await Task.Delay(delay, cancellationToken);
}
}
See an example in Microsofts documentation.
Related
If I start the following example in .NET Core BackgroundService on debug mode:
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Task.Run(async () => await Task.Delay(30000, stoppingToken))
.Wait(stoppingToken);
}
}
the Ctrl + C cancellation event does not call the StopAsync() Method which is calling the Cancel() from the CancellationTokenSource.
I think my problem is similar to this post.
How can I catch those cancellations when I'm using blocking methods inside the ExecuteAsync?
p.s.: In the real world my ExecuteAsync is watching the filesystem until a new file is created in my destination. To achieve this behavior I'm using the FileSystemWatcher.WaitForChanged() method.
From the comments, it looks like the problem has little to do with threads. The real problem is how to stop a FileSystemWatcher.
You don't need an extra thread with a FileSystemWatcher, you need to handle its change events as quickly as possible. You can use an asynchronous event handler for this, or even better, quickly post events to a queue or Channel for processing.
To stop the FSW you can use the CancellationToken.Register method to set EnableRaisingEvents to false :
stoppingToken.Register(()=>watcher.EnableRaisingEvents=false);
Event processing
To quickly handle events, one could post the FileSystemEventArgs values directly to a queue or a Channel and process them with another tasks. This has two benefits:
File events are handled as fast as possible, so none is lost
The code can either wait for all events to finish, or cancel them.
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
A Channel can be treated as a queue with asynchronous read and write operation. The ReadAllAsync method dequeues messages until stopped and returns them as an IAsyncEnumerable which allows the use of await foreach to easily handle items asynchronously.
Pipelines and Channels
The code can be refactored into this:
await watcher.AsChannel(stoppingToken)
.ProcessEvents(stoppingToken);
The consumer
It's easy to extract the subscriber code into a separate method. This could even be an extension method:
public static async Task ProcessEvents(this ChannelReader<FileSystemEventArgs> reader,CancellationToken stoppingToken)
{
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
}
And call it :
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
await ProcessEvents(channel,stoppingToken);
This works because a Channel has implicit cast operators to ChannelReader and ChannelWriter.
A ChannelReader supports multiple consumers, so one could use multiple tasks to process events, eg :
public static async Task ProcessEvents(this ChannelReader<FileSystemEventArgs> reader,int dop,CancellationToken stoppingToken)
{
var tasks=Enumerable.Range(0,dop).Select(()=>{
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
});
await Task.WhenAll(tasks);
}
The producer
It's also possible to extract the channel creation and posting into a separate method. After all, we only need the ChannelReader for processing:
public static ChannelReader AsChannel(this FileSystemWatcher watcher, CancellationToken stoppingToken)
{
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
return channel;
}
And combine everything in a simple pipeline:
await watcher.AsChannel(stoppingToken)
.ProcessEvents(stoppingToken);
My first workaround at the moment is this:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var blockingTask = Task.Run(async () => await Task.Delay(30000, stoppingToken));
await Task.WhenAny(blockingTask);
}
}
#Panagiotis Kanavos I appreciate your efforts, I'm coming back to your detailed post If I'm trying to change my "blocking" FSW to an event driven FSW.
In productive I'm using something like this:
private void DoServiceWork()
{
// Some Work if new PDF or docx file is available
// ...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
int myTimeout = 1000 * 60 * 60; // 1 hour
while (!stoppingToken.IsCancellationRequested)
{
pdfWatchingTask = Task.Run(() => MyFSWLibrary.Watch(directory, "*.pdf", myTimeout, stoppingToken));
docWatchingTask = Task.Run(() => MyFSWLibrary.Watch(directory, "*.docx", myTimeout, stoppingToken));
var finishedTask = await Task.WhenAny(new Task<MyFSWResult>[] { waitPdfTask, waitXmpTask });
if(finishedTask.Result.Success) DoServiceWork();
}
}
Here is an example code for loop inside ExecuteAsync method of MyWorkerclass that inherits from BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested){
// Do some work
// Sleep for 1s
await Task.Delay(1000, stoppingToken);
}
// Do some work after stopping token is canceled
this.WorkThatNeverExecutes()
}
The problem is that after stoppingToken is canceled, the method WorkThatNeverExeuctes is never executed.
After investigating source code of the BackgroundService I've noticed the following:
In its StopAsync method, it is waiting until either my own background service (its exeutingTask) is finished or cancellationToken of the background service is canceled(it will be after a short delay):
What is happening here I think is my await Task.Delay after the stoppingToken is canceled, makes the executingTask completed and the parent BackgroundService exits. I would like to know a way around this so my ExecuteAsync is executed completely before returning. Also a way that does not include not passing stoppingToken to my Delay method or something similar (which would work).
// Inside BackgroundService.cs
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
So as the #Ralf suggested the problem is that Task.Delay(1000, stoppingToken) throws
TaskCanceledException so my code does not continue. The solution is to catch given exception and the convinient one liner is to wrap my Task.Delay into Task.WhenAny like this
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested){
// Do some work
// Sleep for 1s
await Task.WhenAny(Task.Delay(1000, stoppingToken));
}
// Do some work after stopping token is canceled
this.ThisWillExecuteAfterStoppingTokenIsCanceled()
}
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);
}
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).
I want to launch a method in a separated thread periodically (every minute). I was using System.Timers.Timer but I realize that Timers cause memory leaks.
I need to remove Timers and use task. The first idea is launch a Task in the following way:
_taskWork = Task.Factory.StartNew( DoWork );
And in the DoWork method:
private void DoWork()
{
While(true)
{
// Stuff here
Thread.Sleep(60000);
}
}
Is there any way to launch a task avoiding this approach?
Similar Method : The async equivalent is a while loop with Task.Delay (which internally uses a System.Threading.Timer):
public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken)
{
while (true)
{
await FooAsync();
await Task.Delay(interval, cancellationToken)
}
}
This Work fine for me please refer this for more understanding.
Is there any chance to avoid waiting?
What we want for example:
async Task SomeTask()
{
await ChildTask();
//Then something we want to be done without waiting till "Child Task" finished
OtherWork();
}
async Task ChildTask()
{
//some hard work
}
Capture the Task and then await it after OtherWork is done:
async Task SomeTask()
{
var childTask = ChildTask();
//Then something we want to be done without waiting till "Child Task" finished
OtherWork();
await childTask;
}
You're not forced to await an asynchronous Task. If you don't await it, it's because you don't care if it finishes successfully or not (fire and forget approach).
If you do so, you shouldn't use the async keyword in your method/delegate signatures.