Alf*_*fie 5 .net c# garbage-collection timer system.timers.timer
在下面的代码中,a Timer在函数内声明,它也订阅了Elapsed事件:
void StartTimer()
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.AutoReset = false;
timer.Start();
}
Run Code Online (Sandbox Code Playgroud)
一旦函数完成,对该函数的引用Timer就会丢失(我推测).
Elapsed事件是否在对象被销毁时自动取消注册,如果没有,事件可以在Elapsed事件本身中取消注册:
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var timer = (System.Timers.Timer)sender;
timer.Elapsed -= timer_Elapsed;
}
Run Code Online (Sandbox Code Playgroud)
这种方法是否仍允许正确清理对象,或者为什么定时器永远不会在函数的本地范围内声明?
Han*_*ant 14
这里的规则很复杂,你无法看到CLR里面发生了什么.其中维护了一个活动计时器列表,System.Timers.Timer在该列表中有一个引用,使其保持活动状态并防止它被垃圾收集.在您的情况下是必要的,因为您的StartTimer()方法中的本地变量不足以使其保持活动状态.
使用AutoReset = false时,CLR会在计时器从列表中删除计时器.剩下的唯一引用是Elapsed事件处理程序中的sender参数.
如果您没有使用发送方显式重新启用计时器,因此将其放回CLR队列,则Timer对象没有任何引用.只要GC运行,它就会被垃圾收集.
取消订阅Elapsed事件处理程序对此没有影响.这是另一个很难看到的细节,您的活动订阅添加了对此的引用.换句话说,Timer对象实际上使您的外部对象保持活动状态.这当然是一件好事,你不希望你的对象被垃圾收集,而计时器仍然可以调用你的Elapsed事件处理程序.如果你希望对象的生命周期没有被计时器延长,那么你将不得不做更多的工作.现在有必要显式取消订阅事件处理程序并停止计时器.这需要您保留对Timer对象的引用.
还要记住,如果你的类实现了IDisposable本身,那么它也应该处理Timer.这是必要的,因为您通常不希望Elapsed事件处理程序在已处置的对象上运行,这往往会触发ObjectDisposedExceptions.再次保持将Timer对象引用存储在类的字段中的原因.一定要提防这就是地板垫下隐藏着非常讨厌穿比赛的错误,经过的情况下仍然可以运行后,或当你调用计时器的Dispose()方法.需要联锁以防止一年一月或一个月的蓝月亮使您的程序崩溃.与允许代码在工作线程上运行并访问共享状态时必须采取的正常预防措施不同.
总而言之,如果你没有进一步使用Timer,那么将它放在Elapsed事件处理程序中是合乎逻辑的事情.实际上并不是必需的,不活动的计时器不会消耗系统资源,但.NET程序员通常对跳过它非常不舒服.再一次线程竞赛是可能的,你可能会配置一个已经处理好的计时器,但这不会造成麻烦.