【问题标题】:What is the best way to handle several timers in the same class?在同一个类中处理多个计时器的最佳方法是什么?
【发布时间】:2014-11-30 00:59:03
【问题描述】:

我在课堂上使用了几个计时器,并且想知道实现它的两个选项:

选项 1:

每条消息都有自己的计时器。

 class MessagesManager1
{
    Timer _timerA = new Timer();
    Timer _timerB = new Timer();
    Timer _timerC = new Timer();

    public MessagesManager1()
    {
        _timerA.Interval = 1000; // 1 sec
        _timerB.Interval = 3000; // 3 sec
        _timerC.Interval = 5000; // 5 sec

        _timerA.Elapsed += _timerA_Elapsed;
        _timerB.Elapsed += _timerB_Elapsed;
        _timerC.Elapsed += _timerC_Elapsed;

        _timerA.Start();
        _timerB.Start();
        _timerC.Start();
    }

    void _timerA_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message A
    }

    void _timerB_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message B
    }

    void _timerC_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message C
    }
}

选项 2: 只使用一个定时器,每条消息的发送取决于 secondsCounter 的模数结果。

 class MessagesManager2
{
    Timer _timer = new Timer();

    public MessagesManager2()
    {
        _timer.Interval = 1000; // 1 sec

        _timer.Elapsed += _timer_Elapsed;
        _timer.Start();
    }

    int secondsCounter;

    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message A

        if (secondsCounter % 3 == 0)
        {
            // send message B
        }

        if (secondsCounter % 5 == 0)
        {
            // send message C
        }
    }
}

哪个更好,为什么? (在性能、架构、维护、最佳实践等方面)。

【问题讨论】:

  • 出于可维护性原因的选项 1,但实际上这取决于您,因为您将处理它。
  • 您能否提供更多详细信息(例如,您将拥有的预期计时器数量的上限)?一般来说,越容易阅读或理解的案例越好,所以可能是您的首选。
  • 没有足够的上下文来提供一个好的答案。您的第二个选择是矫枉过正。选项 #1 可以正常工作。但这就是所有可以说的。您要求讨论“为什么”?无论如何,这个问题太宽泛了,但无论如何这里都没有足够的细节来真正解决这个问题。
  • 也许没有足够的信息来提供关于性能的建设性答案,但在架构、维护和最佳实践方面还有很多工作要做。

标签: c# .net architecture


【解决方案1】:

我不会说这些。

您应该遵循单一职责原则 - 在您的两种选择中,同一个班级正在执行不止一件事。

我将创建一个具有单个 Timer 的类,然后在运行时创建该类的 3 个实例,将间隔值传递给构造函数。

根据需要在 Elapsed 事件中执行的操作,您可以将 Action 委托传递给构造函数,或者创建派生类来覆盖具有所需行为的抽象/虚拟方法。

方法 1

如果你知道你只会处理这三件事,那么我会说最好在编译时将它们定义为显式行为:

public abstract class BaseTimerHandler
{
    private readonly Timer m_Timer;

    public BaseTimerHandler(int interval)
    {
        m_Timer = new Timer();
        m_Timer.Interval = interval;
        m_Timer.Elapsed += TimerElapsed;
        m_Timer.Start();
    }

    protected abstract TimerElapsed(object sender, ElapsedEventArgs e);

    //TODO unsubscribe from Elapsed in Dispose method.
}

public class TimerAHandler : BaseTimerHandler
{
    private const int _INTERVAL = 1000;
    public TimerAHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message A
    }
}

public class TimerBHandler : BaseTimerHandler
{
    private const int _INTERVAL = 3000;
    public TimerBHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message B
    }
}

public class TimerCHandler : BaseTimerHandler
{
    private const int _INTERVAL = 5000;
    public TimerCHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message C
    }
}

方法 2

如果您正在谈论这些计时器的数量可变,可能在运行时定义,那么我将注入行为而不是定义显式类(如果您需要发送者或事件参数,请调整 Action 委托以包含参数)。

public class TimerHandler
{
    private readonly Timer m_Timer;
    private readonly Action m_Elapsed;

    public BaseTimerHandler(int interval, Action sendMessage)
    {
        if (sendMessage == null)
            throw new ArgumentNullException("sendMessage", "Must provide send message");

        m_Timer = new Timer();
        m_Timer.Interval = interval;
        m_Timer.Elapsed += TimerElapsed;

        m_Elapsed = sendMessage;

        m_Timer.Start();
    }

    private TimerElapsed(object sender, ElapsedEventArgs e){
    {
        m_Elapsed();
    }

    //TODO unsubscribe from Elapsed in Dispose method.
}

var timerA = new TimerHandler(1, () => { //send message A });
var timerB = new TimerHandler(3, () => { //send message B });
var timerC = new TimerHandler(5, () => { //send message B });

这似乎有点矫枉过正,但我​​假设您的示例已简化。在这两种情况下,优点是:

  • 您可以将计时器处理与要在 Elapsed 中执行的实际代码分开进行单元测试(在 BaseTimerHandler 中,您可以为单元测试创​​建一个 Mock 派生类型,在 TimerHandler 中您可以注入一个假的Action)。
  • 您已删除重复的计时器代码,因此更易于维护并确保一致性。
  • 如果Elapsed中要执行的代码也包含一些公共代码,那么可以将BaseTimerHandler.TimerElapsed扩展为虚方法并添加该公共代码,或者对于TimerHandler,扩展TimerElapsed包含公共代码.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-26
    • 2018-02-14
    • 1970-01-01
    • 2011-02-26
    • 2015-02-03
    • 1970-01-01
    • 2018-12-01
    • 2017-01-14
    相关资源
    最近更新 更多