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

C# 在类的所有实例中同步计时器的好方法是什么?,c#,timer,singleton,static-classes,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

也就是说,我有一个包含静态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
    }
}

由于计时器引用存储在
静态
字段中,因此一次只能有一个计时器实例可用。所以我不清楚你所说的“同步计时器[复数]”是什么意思

如果希望类的每个实例在单个计时器的同一个刻度上执行其
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
    作为值传递给构造函数的
    state
    参数。现在还不清楚你打算怎么做。下面是实际发生的情况:计时器第一次初始化时,字段的值是
    null
    ,因此
    state
    参数是
    null
    ,这就是传递给处理程序的内容。对于
    TestClass
    的每个新实例,该实例将创建一个新计时器,但使用先前创建的计时器实例作为
    状态
    值。在
    TestClass
    的每个实例中,当调用其
    DoStuff()
    方法时,它将接收到由
    TestClass
    对象的前一个实例创建的计时器的引用,或者为创建的
    TestClass
    对象的第一个实例的
    null

    ,我只是在没有
    状态
    值的情况下初始化计时器(传递
    null
    ),然后在调用委托时传递计时器引用本身。这对我来说比你的代码所做的更有意义
  • 静态事件总是必须非常小心地使用,因为它们本身总是存在的,并且它们隐式地保留对订阅该事件的任何对象的引用。使用上述方法,您的
    TestClass
    对象将永远不会被垃圾收集,因为没有从计时器事件中取消订阅对象的机制。您可能想考虑将一个方法添加到您的<代码> TestClass <代码>对象中,该对象在该事件未订阅的情况下,调用代码可以在它准备丢弃一个给定的<代码> TestClass >代码>对象之前调用。
    BR/>这是否是实际需要的,我不知道。你的问题没有足够的背景。如果这些对象永远不需要GC'ed,那么您可以跳过这一步。如果您确实需要这样做,您可能需要考虑实现<代码> IDISPOSTABLE <代码>作为调用该方法的方便机制(即使您的代码>处理()/代码>方法调用它),所以a)可以使用<代码>使用语句来处理对象的生命周期,和b)以便接口实现的存在提醒您需要手动管理对象的生存期。在这里实现
    IDisposable
    的一个警告(除了所有常见的其他警告之外)是,您不能依赖终结器作为错误代码的备份,因为只有当对象实际上符合GC'ed条件时,终结器才起作用,在这种情况下,只有调用
    Dispose()


    此问题的另一个替代方案是使用弱引用实现
    timerhandler
    事件。如果这是在WPF程序的上下文中,则可以使用
    WeakEventManager
    类来简化此过程。如果不是,您可能会发现不值得费心学习如何正确使用弱引用,尤其是在事件上下文中。同样,如果没有更多的背景,我很难说。无论哪种方式,使用弱引用的好处是事件代码保留的引用本身不会阻止对象被GC’ed;好处是,新的挑战更加自动化……一旦解决了它,就完成了,而不必记住每次创建
    TestClass
    对象的实例时,都需要在以后清理它。当然,缺点是从概念上讲,弱引用可能比在丢弃对象之前必须清理对象的基本思想更难理解

这是无效代码。无论如何,请阅读有关“多播代理”的内容,它与
System.Timers.Timer
一起将解决您的问题。谢谢。但有什么是无效的?它应该只是一些重命名,并从实际运行的代码中删除行?看起来您正试图在构造函数中声明成员数据——如果它应该是成员,则将其放入类中;如果它应该是局部变量,则删除
public
static
关键字。啊,是的。固定的。