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

ASP.NET Core循环调用Task.Delay(50)是否会引发内存泄漏?

结论

你当前的代码不存在内存泄漏,这个告警绝大多数情况下可以直接忽略,不需要特殊处理。

告警原因拆解

ReSharper DPA 报的TimerQueueTimer分配是Task.Delay的正常底层行为:

  • 每次调用Task.Delay,.NET 运行时都会在定时器队列新建一个TimerQueueTimer实例,等延迟时间到、关联的Task状态流转为完成后,这个实例就失去引用,等待GC回收
  • 你设置的50ms执行间隔非常短,每秒会创建20个Timer实例,GC不是实时回收内存的,会等到小对象堆分配达到Gen0回收阈值才会批量清理这些短生命周期对象,所以你会看到几MB的当前分配、上百MB的峰值——这只是两次GC间隔内攒下的临时对象,不是泄漏,回收后内存会立刻回落。
提到的「Task.Delay创建快于完成导致泄漏」的场景和你当前代码无关

相关结论里的泄漏问题有严格的触发前提:代码没有await Task.Delay,在循环里无脑触发即忘(fire-and-forget),完全不等待前序Delay任务完成,比如下面这种错误写法才会出现Timer无限堆积:

// 错误写法:没有await,会无限创建Timer导致泄漏
while (!stoppingToken.IsCancellationRequested)
{
    _ = Task.Delay(50, stoppingToken); // 不等待,直接丢出去跑
    DoWork();
}

你现在的代码是await Task.Delay(...),属于顺序执行:上一轮的业务逻辑+Delay完全执行结束前,根本不会进入下一轮循环创建新的Timer,天然不可能出现创建速度快于完成速度的问题。

哪些情况需要你调整?

只有出现以下场景时才需要处理,否则不用管这个告警:

  • 生产环境观测到进程内存持续线性上涨,连续运行数天没有回落,通过内存dump确认大量TimerQueueTimer实例存在持续引用、没有被GC回收
  • 你循环内的业务逻辑执行耗时长期接近甚至超过50ms,导致实际调度间隔达不到预期,任务出现堆积
  • 你对临时分配非常敏感,想彻底消掉这个DPA告警

如果要优化,直接用.NET 6 内置的PeriodicTimer替换原来的Task.Delay循环即可,它内部会复用单个定时器实例,不会每轮产生新的TimerQueueTimer分配,性能更好:

public class DemoWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(50));
        try
        {
            while (await timer.WaitForNextTickAsync(stoppingToken))
            {
                // 执行业务逻辑
                Console.WriteLine(DateTimeOffset.Now);
            }
        }
        catch (OperationCanceledException)
        {
            // 服务停止触发取消,属于正常流程,忽略即可
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:48:22