捕获 BackgroundService 中的异常 | 学学官方是如何实现的

前言

上次,我们实现了《使用“装饰者模式”捕获 BackgroundService 中的异常》。

结果发现,微软已经发现了这个问题,并在 .NET 6 中解决了。(囧)

a1c412e252fbbb1e8bb4c3b5961951c0.png

让我们验证一下:

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

确实会抛出异常并终止程序:

8c1aecfc3abe8ca7d91ed2d9ccca5add.png

那现在让我们学习下,微软官方是如何实现的,对我们以后处理类似问题可以起到借鉴作用。

实现代码

通过查看提交历史[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.Start
    await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
}

现在在启动了 IHostedService 后,还会对 BackgroundService.ExecuteTask 进行try-catch:

foreach (IHostedService hostedService in _hostedServices)
{
    // Fire IHostedService.Start
    await 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

[3]

Host: https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs

添加微信号【MyIO666】,邀你加入技术交流群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值