C# 中有三种定时器, System.Windows.Forms 中的定时器和 System.Timers.Timer 的工作方式是完全一样的, 所以, 这里我们仅讨论 System.Timers.Timer 和 System.Threading.Timer
1, 定时器保活
先来看一个例子:
- class Program
- {
- static void Main(string[] args)
- {
- Start();
- GC.Collect();
- Read();
- }
- static void Start()
- {
- Foo f = new Foo();
- System.Threading.Thread.Sleep(5_000);
- }
- }
- public class Foo
- {
- System.Timers.Timer _timer;
- public Foo()
- {
- _timer = new System.Timers.Timer(1000);
- _timer.Elapsed += timer_Elapsed;
- _timer.Start();
- }
- private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
- {
- WriteLine("System.Timers.Timer Elapsed.");
- }
- ~Foo()
- {
- WriteLine("---------- End ----------");
- }
- }
运行结果如下:
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- ...
在 Start 方法结束后, Foo 实例已经失去了作用域, 按理说应该被回收, 但实际并没有 (因为析构函数没有执行, 所以肯定实例未被回收).
这就是定时器的 保活机制, 因为定时器需要执行 timer_Elapsed 方法, 而该方法属于 Foo 实例, 所以 Foo 实例被保活了.
但多数时候这并不是我们想要的结果, 这种结果导致的结果就是 内存泄露, 解决方案是: 先将定时器 Dispose.
- public class Foo : IDisposable
- {
- ...
- public void Dispose()
- {
- _timer.Dispose();
- }
- }
一个很好的准则是: 如果类中的任何字段所赋的对象实现了 IDisposable 接口, 那么该类也应当实现 IDisposable 接口.
在这个例子中, 不止 Dispose 方法, Stop 方法和设置 AutoReset = false, 都能起到释放对象的目的. 但是如果在 Stop 方法之后又调用了 Start 方法, 那么对象依然会被保活, 即便 Stop 之后进行强制垃圾回收, 也无法回收对象.
System.Timers.Timer 和 System.Threading.Timer 的保活机制是类似的.
保活机制是由于定时器引用了实例中的方法, 那么, 如果定时器不引用实例中的方法呢?
2, 不保活下
System.Timers.Timer
和
System.Threading.Timer
的差异
要消除定时器对实例方法的引用也很简单, 将 timer_Elapsed 方法改成 静态 的就好了.(静态方法属于类而非实例.)
改成静态方法后再次运行示例, 结果如下:
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- ---------- End ----------
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- System.Timers.Timer Elapsed.
- ...
Foo 实例是被销毁了 (析构函数已运行, 打印出了 End), 但定时器还在执行, 这是为什么呢?
这是因为,.NET Framework 会确保 System.Timers.Timer 的存活, 即便其所属实例已经被销毁回收.
如果改成 System.Threading.Timer, 又会如何?
- class Program
- {
- static void Main(string[] args)
- {
- Start();
- GC.Collect();
- Read();
- }
- static void Start()
- {
- Foo2 f2 = new Foo2();
- System.Threading.Thread.Sleep(5_000);
- }
- }
- public class Foo2
- {
- System.Threading.Timer _timer;
- public Foo2()
- {
- _timer = new System.Threading.Timer(timerTick, null, 0, 1000);
- }
- static void timerTick(object state)
- {
- WriteLine("System.Threading.Timer Elapsed.");
- }
- ~Foo2()
- {
- WriteLine("---------- End ----------");
- }
- }
注意, 这里的 timerTick 方法是静态的. 运行结果如下:
- System.Threading.Timer Elapsed.
- System.Threading.Timer Elapsed.
- System.Threading.Timer Elapsed.
- System.Threading.Timer Elapsed.
- System.Threading.Timer Elapsed.
- ---------- End ----------
可见, 随着 Foo2 实例销毁,_timer 也自动停止并销毁了.
这是因为,.NET Framework 不会保存激活 System.Threading.Timer 的引用, 而是直接引用回调委托.
来源: https://www.cnblogs.com/gl1573/p/12267800.html