Search code examples
c#.net-coreasp.net-core-hosted-services

Hosted BackgroundService StopAsync not executing after await


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

        }

Solution

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