.NET Core 2.1中如何全局捕获异常避免程序崩溃?
.NET Core 2.1 后台服务全局异常捕获方案
针对你遇到的后台服务未处理异常导致程序崩溃的问题,以下是可行的解决方法:
1. 在后台服务执行逻辑中直接捕获异常
.NET Core 2.1 中BackgroundService(IHostedService的抽象实现)的核心执行逻辑在ExecuteAsync方法里,直接在这个方法内包裹全局try-catch,就能捕获业务逻辑抛出的异常,阻止其冒泡到主机进程导致崩溃:
public class MyBackgroundService : BackgroundService { private readonly ILogger<MyBackgroundService> _logger; public MyBackgroundService(ILogger<MyBackgroundService> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { // 替换为你的后台业务逻辑 await RunBusinessLogic(stoppingToken); } catch (Exception ex) { // 记录异常日志 _logger.LogError(ex, "后台服务执行发生未处理异常"); // 可选:异常后延迟重试,避免频繁报错 await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken); } } } private async Task RunBusinessLogic(CancellationToken stoppingToken) { // 你的业务代码 } }
这种方式最直接,适合单个或少量后台服务的场景。
2. 自定义统一异常包装器,批量处理所有后台服务
如果有多个后台服务,不想逐个修改,可以实现一个IHostedService的包装类,统一处理所有被包裹服务的启动、执行、停止阶段的异常:
public class HostedServiceExceptionWrapper : IHostedService { private readonly IHostedService _innerService; private readonly ILogger<HostedServiceExceptionWrapper> _logger; private Task _executingTask; private CancellationTokenSource _stoppingCts; public HostedServiceExceptionWrapper(IHostedService innerService, ILogger<HostedServiceExceptionWrapper> logger) { _innerService = innerService; _logger = logger; } public async Task StartAsync(CancellationToken cancellationToken) { _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); try { // 启动内部服务 await _innerService.StartAsync(_stoppingCts.Token); // 如果是BackgroundService,单独处理ExecuteAsync的异常 if (_innerService is BackgroundService backgroundService) { _executingTask = ExecuteWithExceptionHandling(backgroundService, _stoppingCts.Token); } } catch (Exception ex) { _logger.LogError(ex, "后台服务启动失败"); } } private async Task ExecuteWithExceptionHandling(BackgroundService service, CancellationToken stoppingToken) { try { await service.ExecuteAsync(stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "后台服务执行过程中发生未处理异常"); // 可选:可以在这里尝试重启服务,或者做其他恢复操作 } } public async Task StopAsync(CancellationToken cancellationToken) { try { _stoppingCts?.Cancel(); if (_executingTask != null) { await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); } await _innerService.StopAsync(cancellationToken); } catch (Exception ex) { _logger.LogError(ex, "后台服务停止失败"); } finally { _stoppingCts?.Dispose(); } } }
注册服务时用这个包装器包裹你的实际后台服务:
services.AddTransient<MyBackgroundService>(); services.AddHostedService<HostedServiceExceptionWrapper>(provider => new HostedServiceExceptionWrapper( provider.GetRequiredService<MyBackgroundService>(), provider.GetRequiredService<ILogger<HostedServiceExceptionWrapper>>()));
关于AppDomain.CurrentDomain.FirstChanceException的说明
这个事件只是在异常第一次被抛出时触发通知,它不会阻止异常的传播,所以即便捕获到事件,异常依然会继续向上冒泡,最终导致程序崩溃。它的主要作用是调试时追踪所有异常的抛出点,或者记录所有异常信息,但无法解决崩溃问题。
内容的提问来源于stack exchange,提问作者Ivan Law
相关产品推荐
相关产品推荐

