前言
上次,我们实现了《使用“装饰者模式”捕获 BackgroundService 中的异常》。
结果发现,微软已经发现了这个问题,并在 .NET 6 中解决了。(囧)
让我们验证一下:
using IHost host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddHostedService<DemoBackgroundService>();}).Build();await host.RunAsync();public class DemoBackgroundService : BackgroundService
{protected override async Task ExecuteAsync(CancellationToken stoppingToken){Console.WriteLine("DemoBackgroundService.ExecuteAsync");await Task.Delay(5000);throw new Exception("DemoBackgroundService throw Exception");}
}
确实会抛出异常并终止程序:
那现在让我们学习下,微软官方是如何实现的,对我们以后处理类似问题可以起到借鉴作用。
实现代码
通过查看提交历史[1]。我们发现如下修改:
BackgroundService
BackgroundService[2] 并没有较大修改,只是将 _executingTask
改名为 _executeTask
,并暴露出去:
/// <summary>
/// Gets the Task that executes the background operation.
/// </summary>
/// <remarks>
/// Will return <see langword="null"/> if the background operation hasn't started.
/// </remarks>
public virtual Task ExecuteTask => _executeTask;
Host
关键改动是在 Host[3]
原来是仅仅启动了 IHostedService
:
foreach (IHostedService hostedService in _hostedServices)
{// Fire IHostedService.Startawait hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
}
现在在启动了 IHostedService
后,还会对 BackgroundService.ExecuteTask
进行try-catch
:
foreach (IHostedService hostedService in _hostedServices)
{// Fire IHostedService.Startawait hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);if (hostedService is BackgroundService backgroundService){_ = TryExecuteBackgroundServiceAsync(backgroundService);}
}private async Task TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)
{Task backgroundTask = backgroundService.ExecuteTask;...try{await backgroundTask.ConfigureAwait(false);}catch (Exception ex){...if (_options.BackgroundServiceExceptionBehavior == BackgroundServiceExceptionBehavior.StopHost){_logger.BackgroundServiceStoppingHost(ex);_applicationLifetime.StopApplication();}}
}
关键之处在于,执行TryExecuteBackgroundServiceAsync
时并没有加上await
,也就不会阻塞后续代码的执行;但在方法内部使用了await backgroundTask
等待 ExecuteTask 执行完成。所以当 ExecuteTask
出错时,能够截获错误并执行终止当前应用程序操作。
参考资料
[1]
提交历史: https://github.com/dotnet/runtime/pull/50569/files#diff-47e4bfc6ce357641a2b2f5e94e6981fb5ceadcb8e22d67060564b5ed5f44ceb0
[2]BackgroundService
: https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs
Host
: https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs
添加微信号【MyIO666】,邀你加入技术交流群