什么是在类的所有实例中同步计时器的好方法?

时间:2017-02-20 22:51:44

标签: c# timer singleton static-classes

也就是说,我有一个包含静态System.Threading.Timer的类,我想在所有对象中同步定时器。一个例子如下,我想要实现的是让所有对象同时调用DoStuff()

public class TestClass {
    public static Timer timer;
    public TestClass() {
        TimerCallback callback = DoStuff;
        timer = new Timer(callback, timer, 0, 500);
    }

    public void DoStuff(object source) {
    // Do stuff
    }
}

1 个答案:

答案 0 :(得分:1)

由于计时器参考存储在static字段中,因此您一次只能使用一个计时器实例。因此,我不清楚"同步定时器[复数]" 是什么意思。

如果您希望类的每个实例在单个计时器的相同标记上执行其DoStuff()方法,那么在我看来,正确的方法是维护单个静态处理程序,调用每个实例更新的委托实例。例如:

public class TestClass {

    private static class TimerHandler
    {
        public static event TimerCallback TimerHandlers;

        private static readonly Timer timer = new Timer(TimerHandlerCallback, null, 0, 500);

        private static void TimerHandlerCallback(object state)
        {
            TimerHandlers?.Invoke(timer);
        }
    }

    public TestClass() {
        TimerHandler.TimerHandlers += DoStuff;
    }

    public void DoStuff(object source) {
    // Do stuff
    }
}

注意:

  • 无论你做什么,我认为没有理由建立Timer字段public
  • 我将计时器包装在一个嵌套的静态类中,因为这样做允许我用一个事件公开计时器,这反过来允许我忽略线程安全修改回调委托的问题,因为编译器会自动地生成必要的代码。
  • 您最初将timer作为值传递给构造函数的state参数。目前还不清楚你打算做什么。以下是所做的:第一次初始化计时器时,字段的值为null,因此state参数为{{1那是什么传递给处理程序的。对于null的每个新实例,该实例将创建一个新计时器,但使用以前创建的计时器实例作为TestClass值。在state的每个实例中,当调用其TestClass方法时,它将接收对前一个DoStuff()对象实例创建的计时器的引用,或者TestClass创建null对象的第一个实例。

    相反,我只是初始化没有TestClass值的计时器(传递state),然后传递计时器在调用委托时引用它自己。这对我来说比你的代码所做的更有意义。
  • 必须非常谨慎地使用静态事件,因为它们本身始终存在,并且它们隐式地保留对订阅该事件的任何对象的引用。有了上述内容,您的null对象永远将被垃圾收集,因为没有机制可以从计时器事件中取消订阅对象。您可能需要考虑向您的TestClass对象添加一个方法,该方法取消订阅该事件,以及调用代码在准备丢弃给定的TestClass对象之前可以调用的方法。 />这是否真的需要,我无从知晓。你的问题中没有足够的背景。如果这些对象永远不需要GC,那么您可以跳过此步骤。如果您确实需要这样做,您可能需要考虑实现TestClass作为调用方法的便捷机制(即让您的IDisposable方法调用它),以便a)您可以使用{{ 1}}语句来处理对象的生命周期,以及b)使接口实现的存在提醒您需要手动管理对象的生命周期。在这里实现Dispose()的一个警告(除了所有常见的其他注意事项)是你不能依赖终结器作为错误代码的备份,因为这只适用于实际的对象有资格成为GC,在这种情况下,直到你致电using为止。

    这个问题的另一个替代方案是实施{{1使用弱引用的事件。如果这是在WPF程序的上下文中,您可以使用IDisposable类来简化这一过程。如果不是,您可能会发现学习如何正确使用弱引用并不值得,尤其是在事件的上下文中。再说一次,没有更多的背景,我很难说。无论哪种方式,使用弱引用的好处是事件代码保留的引用本身不会阻止对象被GC编辑。您将一个实现细节挑战替换为另一个;好处是新的挑战更加自动......一旦你解决了它,你就完成了,而不是每次你创建一个你需要清理的Dispose()对象的实例时都要记住它后来。缺点当然是概念上,弱的引用可能比在丢弃对象之前必须清理对象的基本思想更难理解。