Hosted BackgroundService StopAsync not executing after await - c#

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()
}

Related

Await inside System.Threading.Timer callback

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.

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);
}
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).

Cancel long running operation on client cancel

I started my first project with asp.net core and razor pages.
On a client request, a long running database operation will be started.
Now i want to recognize, when the user leaves the website, so the database operation can be cancelled.
I've tried it with a cancellationToken, but it will never be cancelled.
public async Task<JsonResult> OnPostReadAsync([DataSourceRequest] DataSourceRequest request, CancellationToken cancellationToken)
{
var messages = await _logMessageService.GetLogMessagesAsync(request, cancellationToken);
return new JsonResult(messages.ToDataSourceResult(request));
}
The function get called by an Telerik Kendo UI Grid.
Can you tell me, why the cancellation token don't get cancelled, or what other options I have to detect a abortion by the client?
Edit 1
I pass the Token through to this function call of a NpgsqlCommand:
var dataReader = await command.ExecuteReaderAsync(cancellationToken);
To cancel IO bound i.e. task which is running long , following is code that you can do which i got form the C# with CLR book.
Design extension method for task as below.
private static async Task<TResult> WithCancellation<TResult>(this Task<TResult> originalTask,
CancellationToken ct) {
// Create a Task that completes when the CancellationToken is canceled
var cancelTask = new TaskCompletionSource<Void>();
// When the CancellationToken is canceled, complete the Task
using (ct.Register(
t => ((TaskCompletionSource<Void>)t).TrySetResult(new Void()), cancelTask)) {
// Create a Task that completes when either the original or
// CancellationToken Task completes
Task any = await Task.WhenAny(originalTask, cancelTask.Task);
// If any Task completes due to CancellationToken, throw OperationCanceledException
if (any == cancelTask.Task) ct.ThrowIfCancellationRequested();
}
// await original task (synchronously); if it failed, awaiting it
// throws 1st inner exception instead of AggregateException
return await originalTask;
}
as given in following example code you can cancel it by using extension method designed above.
public static async Task Go() {
// Create a CancellationTokenSource that cancels itself after # milliseconds
var cts = new CancellationTokenSource(5000); // To cancel sooner, call cts.Cancel()
var ct = cts.Token;
try {
// I used Task.Delay for testing; replace this with another method that returns a Task
await Task.Delay(10000).WithCancellation(ct);
Console.WriteLine("Task completed");
}
catch (OperationCanceledException) {
Console.WriteLine("Task cancelled");
}
}
In this example cancellation done based on given time , but you can call cancellation by calling cancel method.
So I found the answer myself after some more research.
The problem is a bug in IISExpress as mentioned here: https://github.com/aspnet/Mvc/issues/5239#issuecomment-323567952
I switched to Kestrel and now everything works as expected.

Launch Task periodically

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.

How Do I Create a Looping Service inside an C# Async/Await application?

I have written a class with a method that runs as a long-running Task in the thread pool. The method is a monitoring service to periodically make a REST request to check on the status of another system. It's just a while() loop with a try()catch() inside so that it can handle its own exceptions and and gracefully continuing if something unexpected happens.
Here's an example:
public void LaunchMonitorThread()
{
Task.Run(() =>
{
while (true)
{
try
{
//Check system status
Thread.Sleep(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
});
}
It works fine, but I want to know if there's another pattern I could use that would allow the Monitor method to run as regular part of a standard Async/Await application, instead of launching it with Task.Run() -- basically I'm trying to avoid fire-and-forget pattern.
So I tried refactoring the code to this:
public async Task LaunchMonitorThread()
{
while (true)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
But when I try to call the method in another async method, I get the fun compiler warning:
"Because this call is not awaited, execution of the current method continues before the call is completed."
Now I think this is correct and what I want. But I have doubts because I'm new to async/await. Is this code going to run the way I expect or is it going to DEADLOCK or do something else fatal?
What you are really looking for is the use of a Timer. Use the one in the System.Threading namespace. There is no need to use Task or any other variation thereof (for the code sample you have shown).
private System.Threading.Timer timer;
void StartTimer()
{
timer = new System.Threading.Timer(TimerExecution, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
}
void TimerExecution(object state)
{
try
{
//Check system status
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
From the documentation
Provides a mechanism for executing a method on a thread pool thread at specified intervals
You could also use System.Timers.Timer but you might not need it. For a comparison between the 2 Timers see also System.Timers.Timer vs System.Threading.Timer.
If you need fire-and-forget operation, it is fine. I'd suggest to improve it with CancellationToken
public async Task LaunchMonitorThread(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000, token);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
besides that, you can use it like
var cancellationToken = new CancellationToken();
var monitorTask = LaunchMonitorThread(cancellationToken);
and save task and/or cancellationToken to interrupt monitor wherever you want
The method Task.Run that you use to fire is perfect to start long-running async functions from a non-async method.
You are right: the forget part is not correct. If for instance your process is going to close, it would be neater if you kindly asked the started thread to finish its task.
The proper way to do this would be to use a CancellationTokenSource. If you order the CancellationTokenSource to Cancel, then all procedures that were started using Tokens from this CancellationTokenSource will stop neatly within reasonable time.
So let's create a class LongRunningTask, that will create a long running Task upon construction and Cancel this task using the CancellationTokenSource upon Dispose().
As both the CancellationTokenSource as the Task implement IDisposable the neat way would be to Dispose these two when the LongRunningTask object is disposed
class LongRunningTask : IDisposable
{
public LongRunningTask(Action<CancellationToken> action)
{ // Starts a Task that will perform the action
this.cancellationTokenSource = new CancellationTokenSource();
this.longRunningTask = Task.Run( () => action (this.cancellationTokenSource.Token));
}
private readonly CancellationTokenSource cancellationTokenSource;
private readonly Task longRunningTask;
private bool isDisposed = false;
public async Task CancelAsync()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
await this.longRunningTask;
}
// for completeness a non-async version:
public void Cancel()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
this.longRunningTask.Wait;
}
}
Add a standard Dispose Pattern
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{ // cancel the task, and wait until task completed:
this.Cancel();
this.IsDisposed = true;
}
}
Usage:
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
The Action {...} has a CancellationToken as input parameter, your function should regularly check it
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
Instead of checking for Token, you could call token.ThrowIfCancellationRequested. This will throw an exception that you'll have to catch

Categories