You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用BackgroundService实现定时通知时程序意外终止问题排查

问题分析与解决方案

你的核心问题是BackgroundService执行到notificationService.AddAsync时程序意外终止,移除该行则恢复正常,这大概率是未捕获的异常导致服务崩溃,再加上你清空了日志提供者,无法看到错误信息,才会显得无征兆终止。以下是针对性的修复步骤:

1. 恢复日志记录,排查根本错误

你在配置里清空了所有日志提供者,导致异常信息完全无法输出,这是排查问题的最大障碍。修改Program.cs的日志配置,至少保留控制台日志:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        }).ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole(); // 添加控制台日志,用于查看异常
        }).ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<BackGroundWorkerService>();
        });

重启程序后,控制台会输出AddAsync执行时的异常信息,这能直接定位到UnitOfWork或数据库操作的问题。

2. 修复BackgroundService的核心问题

你的ExecuteAsync存在两个致命问题:

  • 使用Thread.Sleep阻塞线程,会导致服务无法响应取消信号,且浪费线程资源
  • 没有异常捕获,单个循环的异常会直接终止整个服务

修改后的BackGroundWorkerService代码:

public class BackGroundWorkerService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<BackGroundWorkerService> _logger; // 注入日志

    public BackGroundWorkerService(IServiceProvider serviceProvider, ILogger<BackGroundWorkerService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("后台通知服务已启动");

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using (var scope = _serviceProvider.CreateScope())
                {
                    var notificationService = scope.ServiceProvider.GetRequiredService<INotificationService>();
                    var notificationMessage = NotificationMessageService.GetMessage(
                        NotificationMessageTypes.Added,
                        TableNamesConstants.Expenses,
                        "AA"
                    );

                    await notificationService.AddAsync(notificationMessage, "Notification Title | Try", userId: 1);
                    _logger.LogInformation("通知已成功添加");
                }

                // 使用Task.Delay替代Thread.Sleep,响应取消信号
                await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "添加通知时发生异常,将在10秒后重试");
                // 异常后仍延迟10秒再重试,避免频繁报错
                await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
            }
        }

        _logger.LogInformation("后台通知服务已停止");
    }
}

3. 排查UnitOfWork相关问题

根据你描述的现象,AddAsync内部的UnitOfWork操作大概率是异常根源,常见问题包括:

  • UnitOfWork的事务未正确提交或回滚
  • 数据库连接字符串错误、数据库服务未启动
  • 实体映射错误导致的持久化异常
  • 依赖注入的UnitOfWork生命周期配置错误(比如在Scope外被Dispose)

通过恢复的日志,你能直接看到具体异常信息,针对性修复即可。

关键说明

  • 永远不要在异步服务中使用Thread.Sleep,必须用await Task.Delay,否则会阻塞线程池线程,且无法响应服务的取消请求
  • 后台服务必须添加全局异常捕获,避免单个任务失败导致整个服务终止
  • 日志是排查后台服务问题的核心,绝对不能清空所有日志提供者

内容的提问来源于stack exchange,提问作者Bedirhan Gündöner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 01:50:23