C# System.Threading.Timer 定时任务阻塞及被 GC 回收问题的解决

远山来客
远山来客
发布时间:2026-10-16| 9人浏览过
原创

在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);
}

存在两个主要风险:

  1. 被 GC 回收:局部变量 t 在执行完方法后失去引用,如果未被其他强引用持有,GC 可能会立即将其清理,导致定时任务终止。
  2. 线程阻塞与并发: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# 定时任务优化的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。