Here is an example code for loop inside ExecuteAsync method of MyWorker
class 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()
}