system.threading.timer回调不触发的根本原因是timer实例被gc回收,因其未被强引用持有;必须声明为类级字段(如private static timer _timer)而非局部变量。

System.Threading.Timer 是最直接、最低开销的后台定时任务实现方式,但新手最容易栽在“任务突然不执行”和“进程退出后还在跑”这两件事上——它不会自动管理生命周期,也不会帮你捕获异常。
为什么 System.Threading.Timer 回调不触发?
常见现象:构造完 Timer,控制台没输出,也不报错。
根本原因不是代码写错了,而是你没持有对 Timer 实例的强引用。
-
Timer实例一旦被 GC 回收,回调就永远不会再执行——哪怕间隔时间到了 - 别把
Timer声明成局部变量(比如写在Main()里没加static或字段保存) - 务必声明为类级字段,例如:
private static Timer _timer;或private readonly Timer _timer; - 如果用在
IHostedService中,也要确保字段不被提前释放(StopAsync 里不能只Dispose()就完事)
System.Timers.Timer 的 Elapsed 事件为啥会并发执行?
默认 AutoReset = true,但它的每次回调都可能从线程池拿新线程,不像 System.Threading.Timer 那样复用单一线程。当任务执行慢于间隔时,就会出现多个 Elapsed 并发运行。
- 不加防护直接写日志或更新共享状态,大概率出错
- 用
Interlocked.CompareExchange(ref _isRunning, 1, 0) == 0判断是否可进入 - 或者改用
lock (_lockObj)包裹核心逻辑(注意别锁太久) -
System.Timers.Timer没有Change()方法,想动态调间隔只能Stop()+ 重建
用 IHostedService 做定时任务,StopAsync 里怎么停 Timer?
别调 Dispose(),也别等 await DisposeAsync()——Timer 没这个异步方法,而且 StopAsync 必须返回 Task,不能阻塞。
- 正确做法是调
_timer?.Change(Timeout.Infinite, Timeout.Infinite)(System.Threading.Timer) - 如果是
System.Timers.Timer,就设Enabled = false,再加Stop() -
Dispose()放在Dispose()方法里,不是StopAsync - 记得在
StartAsync中传入cancellationToken,并在回调里检查cancellationToken.IsCancellationRequested
System.Threading.Timer 会静默停止后续触发,而 System.Timers.Timer 会继续尝试下一次——这个行为差异,不实测根本想不到。











