等待后托管的 BackgroundService StopAsync 未执行

Hosted BackgroundService StopAsync not executing after await

这是 MyWorkerclass 的 ExecuteAsync 方法中的循环示例代码,它继承自 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()
        }

问题是取消stoppingToken后,方法WorkThatNeverExeuctes一直没有执行

在调查 BackgroundService 的源代码后,我注意到以下内容: 在它的 StopAsync 方法中,它一直在等待,直到我自己的后台服务(它的 exeutingTask)完成或者后台服务的 cancellationToken 被取消(它会在短暂的延迟之后):

这里发生的事情我认为是我的 await Task.DelaystoppingToken 被取消后,使 executingTask 完成并且父 BackgroundService 退出。 我想知道解决这个问题的方法 所以我的 ExecuteAsync 在返回之前完全执行。还有一种方法,不包括不将 stoppingToken 传递给我的 Delay 方法或类似方法(这会起作用)。

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

        }

正如@Ralf 所建议的那样,问题是 Task.Delay(1000, stoppingToken) 抛出 TaskCanceledException 所以我的代码没有继续。解决方案是捕获给定的异常,方便的一种方式是像这样将我的 Task.Delay 包装成 Task.WhenAny 受保护的覆盖异步任务 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()
    }