时间:2021-05-19
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 的引用,而是直接引用回调委托。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
大家都知道,定时器在TCP可靠传输的过程中起着举足轻重的作用。TCP在建立连接之后可能(保活keep-alive定时器是可选的)会启动四个定时器。 TCP
1.按键字符错乱或无效。按下FN+INS键来尝试解决问题。 问题解决-->数字小键盘开启。 问题未解决-->查杀病毒。 问题解决-->病毒问题引起。
本文实例讲述了C#多线程学习之使用定时器进行多线程的自动管理。分享给大家供大家参考。具体分析如下:Timer类:设置一个定时器,定时执行用户指定的函数。定时器启
事情是这样的:大家都知道“内存泄露”这回事吧。它有几个常见的场景:闭包使用不当引起内存泄漏(未声明的)全局变量分离的DOM节点(随意的)控制台的打印遗忘的定时器
C#中timer类的用法关于C#中timer类在C#里关于定时器类就有3个1.定义在System.Windows.Forms里2.定义在System.Threa