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

后台服务中Timer对象运行异常及依赖注入上下文问题求助

解决BackgroundService中Timer依赖Scope导致的上下文已释放异常

你的核心问题是Timer的生命周期与Scope生命周期不匹配:Timer是长期运行的,但它依赖的IWorkerService是从循环内的短暂Scope中获取的,当Scope被using块自动释放后,IWorkerService及其依赖的上下文(比如DbContext)也被回收,Timer后续触发任务时就会访问已释放的实例,抛出以下异常:

Exception Found: System.InvalidOperationException: Exception in SessionExtendHandle(), Detail : System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection

而把Scope移到循环外长期持有,会导致Scoped服务无法被正常回收,进而引发内存泄漏。

解决方案:让Timer每次执行任务时创建独立Scope

修改TimerObject,不再在构造时注入IWorkerService,而是注入IServiceScopeFactory,每次Timer触发任务时,动态创建新的Scope、获取服务、执行任务,任务完成后自动释放Scope。这样既保证了每次任务都使用有效的上下文,也不会长期持有Scope导致内存泄漏。

1. 重构TimerObject类

public class TimerObject : IDisposable
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private Timer _timer;

    public TimerObject(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }

    public void StartTimer()
    {
        // 根据实际需求设置Timer的触发间隔
        _timer = new Timer(ExecuteTimerTask, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
    }

    private void ExecuteTimerTask(object state)
    {
        // 每次任务执行时创建独立Scope
        using var scope = _serviceScopeFactory.CreateScope();
        var workerService = scope.ServiceProvider.GetRequiredService<IWorkerService>();

        try
        {
            // 执行你的业务逻辑
            workerService.SessionExtendHandle();
        }
        catch (Exception ex)
        {
            // 按需处理异常,比如日志记录
            Console.WriteLine($"Timer任务执行失败: {ex.Message}");
        }
    }

    // 实现IDisposable,清理Timer资源
    public void Dispose()
    {
        _timer?.Dispose();
        GC.SuppressFinalize(this);
    }
}

2. 简化BackgroundService代码

不需要在循环内重复创建Scope,只需要初始化一次Timer,然后等待取消信号即可:

public class ServiceWorker : BackgroundService
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private TimerObject _timerObject;

    public ServiceWorker(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // 只初始化一次Timer
        if (_timerObject == null)
        {
            _timerObject = new TimerObject(_serviceScopeFactory);
            _timerObject.StartTimer();
        }

        // 保持服务运行直到收到取消信号
        await Task.Delay(Timeout.Infinite, stoppingToken);
    }

    // 重写Dispose,清理Timer资源
    public override void Dispose()
    {
        _timerObject?.Dispose();
        base.Dispose();
    }
}

方案优势

  • 每次Timer任务都使用全新的Scope,避免上下文被提前释放的问题;
  • Scope随任务完成自动释放,Scoped服务能被正常回收,不会引发内存泄漏;
  • Timer的生命周期与BackgroundService一致,服务停止时能通过Dispose方法正确清理Timer资源。

内容的提问来源于stack exchange,提问作者Carrie Kaski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:37:01