如何管理在函数范围内声明的Timer的清理? [英] How should the clean-up of Timers declared inside the scope of a function be managed?

查看:130
本文介绍了如何管理在函数范围内声明的Timer的清理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,在函数内部声明了Timer,它还预订了Elapsed事件:

In the following code, a Timer is declared inside a function, where it also subscribes to the 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的引用将会丢失(我想).

Once the function completes, the reference to the Timer is lost (I presume).

Elapsed事件是否随着对象被销毁而自动取消注册,如果没有,是否可以在Elapsed事件本身中取消注册该事件:

Does the Elapsed event get unregistered automatically as the object is destroyed, or if not, can the event be unregistered in the Elapsed event itself:

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        var timer = (System.Timers.Timer)sender;
        timer.Elapsed -= timer_Elapsed;
    }

这种方法是否仍将允许对象的适当清理,或者出于这个原因永远不应该在函数的本地范围内声明计时器?

Would this approach still allow proper cleanup of the object, or should timers never be declared in the local scope of a function for this reason?

推荐答案

这里的规则很复杂,您看不到CLR内部发生了什么.它维护着一个活动计时器的列表,一个System.Timers.Timer在该列表中有一个引用,可以使它保持活动状态并防止其被垃圾回收.在您的情况下,这是必需的,因为StartTimer()方法中的局部变量不足以使其保持活动状态.

The rules here are convoluted, you can't see what's going on inside the CLR. Which maintains a list of active Timers, a System.Timers.Timer has a reference in that list that keeps it alive and prevents it from getting garbage-collected. Necessary in your case since your local variable in your StartTimer() method isn't enough to keep it alive.

在AutoReset = false的情况下,CLR勾选时会将计时器从列表中删除.剩下的 only 引用是Elapsed事件处理程序中的 sender 参数.

With AutoReset = false, the CLR removes the timer from the list when it ticks. The only reference left is the sender argument in your Elapsed event handler.

如果您没有通过使用 sender 显式重新启用计时器,从而将其重新放置在CLR队列中,则没有对Timer对象的引用.每当GC运行时,都会对其进行垃圾收集.

If you don't explicitly re-enable the timer by using the sender, thus putting it back in the CLR queue, then there is no reference left to the Timer object. It will be garbage-collected whenever the GC runs.

取消订阅Elapsed事件处理程序对此没有影响.这是另一个很难看到的细节,您的事件订阅添加了对 this 的引用.换句话说,Timer对象实际上使外部对象保持活动状态.这当然是一件好事,当计时器仍可以调用Elapsed事件处理程序时,您不希望对象被垃圾回收.如果想要对象的寿命没有被计时器延长,那么您将不得不做更多的工作.现在必须明确取消订阅事件处理程序停止计时器.确实需要您保留对Timer对象的引用.

Unsubscribing the Elapsed event handler has no effect on this. That's another detail that is very hard to see, your event subscription added a reference to this. In other words, the Timer object actually keeps your outer object alive. Which is of course a Good Thing, you would not want your object getting garbage collected while the timer can still call your Elapsed event handler. If you want the object's life-time not getting extended by the timer then you'll have to do more work. Now it is necessary to explicitly unsubscribe the event handler and stop the timer. Which does require you to keep a reference to the Timer object.

还请记住,如果您的类本身实现了IDisposable,则它也应该处置Timer.这是必需的,因为您通常不希望Elapsed事件处理程序在已处理的对象上运行,这往往会触发ObjectDisposedExceptions.同样,将Timer对象引用保存在类的字段中的原因也是如此.注意当心隐藏在地板垫下面的令人讨厌的线程竞赛错误,在调用计时器的Dispose()方法后, 之后,Elapsed事件仍然可以运行.需要互锁以防止该程序每年或每月一次因蓝月亮而崩溃.允许代码在工作线程上运行并访问共享状态时,与采取常规预防措施没什么不同.

Also keep in mind that if your class implements IDisposable itself then it should also dispose the Timer. Necessary because you would not typically want the Elapsed event handler to run on a disposed object, that tends to trigger ObjectDisposedExceptions. Again a reason to keep the Timer object reference stored in a field of your class. Do beware the very nasty threading race bug that's hidden under the floor mat, the Elapsed event can still run after or while you call the timer's Dispose() method. Interlocking is required to prevent that from crashing your program once a year or a month with a blue moon. Not otherwise different from the normal precautions you have to take when you allow code to run on a worker thread and access shared state.

总结一下,如果您不再需要使用Timer,那么将其放置在Elapsed事件处理程序中是合乎逻辑的.实际上没有必要,不活动的计时器不会消耗系统资源,但是.NET程序员通常对于跳过它感到非常不自在.再次可能发生线程争用,您可以处置一个已经处置的计时器,但这不会造成麻烦.

Summarizing, if you have no further use for the Timer then disposing it in the Elapsed event handler is the logical thing to do. It isn't actually necessary, a timer that's not active doesn't consume system resources, but .NET programmers are usually very uncomfortable about skipping it. Again a threading race is possible, you might dispose a timer that's already disposed, but that doesn't cause trouble.

这篇关于如何管理在函数范围内声明的Timer的清理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆