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

未持有引用的Scoped服务是否会在作用域结束前被释放?

问题

我想要创建一个在后台运行、按固定间隔执行定时任务的服务TaskTimer。由于它需要在另一个HostedService完成启动后才开始运行,因此无法直接将其注册为HostedService,而是在该HostedService(BotService)内部创建作用域,将其作为Scoped Service获取并运行:

services.AddScoped<TaskTimer>();
services.AddHostedService<BotService>();

该定时服务的实现如下:

using Microsoft.Extensions.Logging;

namespace MyBot;

internal class TaskTimer : IDisposable {
    #region Private vars

    private readonly ILogger<TaskTimer> _log;

    #endregion

    #region Constructors

    public TaskTimer(ILogger<TaskTimer> log) {
        _log = log;

        _log.LogInformation("Starting task timer");

        // [start timer here]
    }

    #endregion

    public void Dispose() {
        _log.LogInformation("Stopping task timer");

        // [stop timer here]
    }
}

随后在主BotService中:

public class BotService : BackgroundService {
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        // [do init here...]

        using (var botServiceScope = _serviceProvider.CreateScope()) {
            // Start task timer
            _ = botServiceScope.ServiceProvider.GetRequiredService<TaskTimer>();

            // [await stoppingToken]
        }
    }
}

目前测试运行正常,但我担心由于丢弃了GetRequiredService的返回值(未持有该服务实例的引用),TaskTimer可能会在using作用域结束前就被释放。请问即使不持有服务实例引用,是否能保证该服务仅在作用域关闭时才被释放?

回答

不能保证。当前测试正常只是偶然情况,这种写法存在隐患:

  • 调用GetRequiredService<TaskTimer>()时,DI容器会创建实例并在作用域内跟踪它,但容器对实例的引用通常不会阻止垃圾回收(GC)。如果你的代码没有持有该实例的强引用,GC随时可能回收它,导致Dispose被提前触发,定时任务意外终止。
  • DI容器的跟踪只是为了在作用域结束时统一执行释放,但它不会主动维持实例的存活——只有代码中的强引用才能确保实例在预期周期内不被回收。

正确做法

必须在作用域内持有TaskTimer的强引用,避免实例被提前GC回收:

public class BotService : BackgroundService {
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        // [do init here...]

        using (var botServiceScope = _serviceProvider.CreateScope()) {
            // 持有强引用,防止GC回收
            var taskTimer = botServiceScope.ServiceProvider.GetRequiredService<TaskTimer>();

            // 等待停止信号,确保作用域与BotService生命周期绑定
            await stoppingToken;
        }
    }
}

这样做的优势:

  1. 强引用会阻止GC在作用域结束前回收TaskTimer实例
  2. 当using块结束(作用域关闭)时,DI容器会自动调用TaskTimer的Dispose方法,确保定时任务被正确停止

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:43:40