在下面的代码中,一个Timer
在函数内部声明,它也订阅了Elapsed
event:
void StartTimer()
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.AutoReset = false;
timer.Start();
}
函数完成后,将引用Timer
丢失了(我猜)。
是否Elapsed
当对象被销毁时,事件会自动取消注册,或者如果没有,事件可以在 Elapsed 事件本身中取消注册:
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var timer = (System.Timers.Timer)sender;
timer.Elapsed -= timer_Elapsed;
}
这种方法是否仍然允许正确清理对象,或者出于这个原因永远不应在函数的本地范围内声明计时器?
这里的规则很复杂,你看不到CLR内部发生了什么。它维护一个活动计时器列表,System.Timers.Timer 在该列表中有一个引用,使其保持活动状态并防止其被垃圾收集。在您的情况下是必要的,因为 StartTimer() 方法中的局部变量不足以使其保持活动状态。
当 AutoReset = false 时,CLR 在计时器滴答时将其从列表中删除。这only左边的参考是senderElapsed 事件处理程序中的参数。
如果您没有使用显式重新启用计时器sender,从而将其放回到 CLR 队列中,那么 Timer 对象就不再有引用了。每当 GC 运行时,它就会被垃圾收集。
取消订阅 Elapsed 事件处理程序对此没有影响。这是另一个很难看到的细节,您的事件订阅添加了对this。换句话说,Timer 对象实际上使外部对象保持活动状态。这当然是一件好事,您不希望您的对象被垃圾收集,而计时器仍然可以调用您的 Elapsed 事件处理程序。如果你want如果计时器没有延长对象的生命周期,那么您将不得不做更多的工作。现在有必要明确地取消订阅事件处理程序and停止计时器。这确实需要您保留对 Timer 对象的引用。
另请记住,如果您的类本身实现了 IDisposable,那么它也应该处理 Timer。这是必要的,因为您通常不希望 Elapsed 事件处理程序在已处置的对象上运行,这往往会触发 ObjectDisposeExceptions。这也是将 Timer 对象引用存储在类的字段中的原因。请注意隐藏在地垫下的非常讨厌的线程竞赛错误,Elapsed 事件仍然可以运行after or while您调用计时器的 Dispose() 方法。需要进行联锁,以防止程序每年或每月一次因蓝月亮而崩溃。与允许代码在工作线程上运行并访问共享状态时必须采取的正常预防措施没有什么不同。
总而言之,如果您不再使用 Timer,那么将其处理在 Elapsed 事件处理程序中是合乎逻辑的做法。实际上没有必要,不活动的计时器不会消耗系统资源,但 .NET 程序员通常不愿意跳过它。同样,线程竞争是可能的,您可能会处置已经处置的计时器,但这不会造成麻烦。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)