C# System.Threading.Timer 定时任务阻塞及被 GC 回收问题的解决
在C#开发中,使用 System.Threading.Timer 实现定时任务时,开发者常遇到任务突然停止(表现为不再执行代码且不打印日志)。这一现象通常由两个核心机制引起:一是局部定义的 Timer 对象被垃圾回收器(GC)意外回收;二是回调方法执行耗时较长,导致线程池中的后续任务排队阻塞,甚至出现多个线程同时执行回调的并发混乱。本文将分析这些问题的成因,并提供标准的修复方案。
基础用法与潜在缺陷
System.Threading.Timer 是 .NET 中轻量级的定时调度工具,它基于线程池工作。其核心构造函数参数如下:
Timer(TimerCallback callback, object state, int dueTime, int period)
callback:执行的回调委托。state:传递给回调的参数对象。dueTime:首次执行前的延时(毫秒)。period:两次执行之间的间隔(毫秒)。
如果在方法内部定义 Timer,如以下示例所示:
protected internal void PollClient()
{
int i=0;
Timer t = new Timer(p => {
i++;
// ... 业务逻辑 ...
}, null, 0, 1000);
}
存在两个主要风险:
- 被 GC 回收:局部变量
t在执行完方法后失去引用,如果未被其他强引用持有,GC 可能会立即将其清理,导致定时任务终止。 - 线程阻塞与并发:
Timer本身仅负责触发回调,不保证回调的执行时间。如果回调体内的业务逻辑耗时超过period设定的间隔,计时器会在上一个回调未完成时再次触发回调,导致多个线程同时执行逻辑,引发竞态条件或资源耗尽。
修正方案一:防止被 GC 回收
最简单的防回收手段是将 Timer 实例提升为类的 static 字段或全局变量。这样对象的生命周期将绑定到类级别,避免被提前销毁。
static Timer _timer = null;
修正方案二:串行化执行并避免线程浪费
为了确保任务不会因执行时间长而堆积在内存中,推荐采用“一次性触发 + 循环内手动重置”的策略。即设置 period 为 Timeout.Infinite,并在每次回调的 finally 块中通过 _timer.Change() 设定下一次触发的时间。
public class TaskScheduler
{
static int i = 0;
static Timer _timer = null;
private readonly ILogger logger = /* Logger instance */;
protected void InitializePollClient()
{
// dueTime: 首次延迟 1000ms; period: 无限(单次执行)
_timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
}
private void TimerCallback(object state)
{
try
{
i++;
// 此处执行业务逻辑
var deviceContext = GetDeviceContext("123456789");
SendMessage(messageList[i % 7], deviceContext.tcpSession.writerContext);
logger.Info("客户端数量:" + deviceContextList.Count + " 循环次数:" + i);
}
catch (Exception e)
{
logger.Error("定时下发报文异常:" + e);
}
finally
{
// 关键步骤:在下一次触发前,重新设置延时
// 这确保了线程在完成当前工作后被释放回线程池,不被长时间占用
if (_timer != null)
_timer.Change(1000, Timeout.Infinite);
}
}
}
原理解析与效果验证
经过上述重构后,定时任务的运行行为将符合预期:
1. **稳定性**:因为 _timer 是静态字段,不会被局部作用域结束所影响,彻底杜绝了“静默失效”的问题。
2. **线程健康**:通过将 period 设为无穷大,只有当代码主动调用 Change 时才会唤醒下一次回调。这使得线程可以在任务完成后立即返回线程池,避免了在高负载场景下创建大量等待执行的线程任务,从而消除了队列阻塞和上下文切换带来的性能损耗。
以上就是 C# 中 System.Threading.Timer 定时任务被阻塞或被 GC 回收问题的详细内容,更多关于 C# 定时任务优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









