前言
上次,我們實現了《使用“裝飾者模式”捕獲 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】,邀你加入技術交流群