【问题标题】:How do I safely dispose a System.Timers.Timer?如何安全地处理 System.Timers.Timer?
【发布时间】:2010-11-25 12:26:40
【问题描述】:

当您处理“原始”.net 计时器时,您可以传入一个等待句柄,以便在 Win32 计时器被销毁后调用,并且您可以假设您的回调不会被调用。 (并且计时器将被 GC 视为“死”)

如何使用 System.Timers.Timer 执行此操作?

【问题讨论】:

    标签: .net multithreading timer dispose


    【解决方案1】:

    在调用 dispose 之前设置一个标志,并在您的 elapsed 处理程序中检查此标志。即使计时器确实触发了,该标志也会阻止任何关联的处理程序代码运行。您可以通过为计时器编写一个包装器来形式化这种模式。

    确保您的标志被标记为 volatile,因为它将被不同的线程访问。

    【讨论】:

    • @Hans 如果在调用 dispose 之前设置了 volatile 标志,那么如果发生后续经过的事件,计时器线程将看到它。您在这里看到的问题是什么?
    • 标志的范围是什么?特别是在包装器的情况下,Stop 方法设置标志并处置计时器。调用 Stop 之后最有可能发生的事情不是包装器本身会超出范围并被 GC 处理,从而使标志无效吗?
    【解决方案2】:

    由于 System.Timers.Timer 和 System.Windows.Forms.Timer 都使用 ThreadPool,它没有操作系统计时器的句柄,因此没有处理本地计时器资源 - 只是一个完整的线程。我不确定您是否可以捕获线程池正在回收的线程,但我可能错了。

    您可以自己滚动(我还没有测试过,在 Dispose 中使用 ManualResetEvent 可能更有用):

    void Run()
    {
        ManualResetEvent resetEvent = new ManualResetEvent(false);
    
        System.Threading.Timer timer = new System.Threading.Timer(delegate { Console.WriteLine("Tick"); });
        timer.Dispose(resetEvent);
    
        MyTimer t = new MyTimer();
        t.Interval = 1000;
        t.Elapsed += delegate { t.Dispose(resetEvent); };
    
        resetEvent.WaitOne();
    }
    
    
    public class MyTimer : System.Timers.Timer
    {
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    
        public virtual void Dispose(WaitHandle handle)
        {
            handle.SafeWaitHandle.Close();
            Dispose();
        }
    }
    

    【讨论】:

    • 我认为 System.Timers.Timer Disposed 事件在调用 System.Threading.Timer Dispose 方法后立即触发。
    • @Ian 该事件在Component.Dispose(disposing) 内部被调用,因此在调用 base.Dispose() 之前向 WaitHandle 发出信号,并使用它应该在之后触发的自定义 Dispose(WaitHandle)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 2013-08-21
    • 2022-11-10
    • 2013-11-03
    • 2017-05-31
    • 2018-05-08
    相关资源
    最近更新 更多