繁体   English   中英

正确使用定时器 class

[英]Using Timer class properly

我想知道我写的代码是否写得正确,它确实是 function,但我以前做过糟糕的设计,所以我需要知道我是否以正确的方式认为这一点。

代码是关于使用 System.Timers.Timer 每 X 小时执行一次重复操作。 我在 stackoverflow 上阅读了一些关于这个主题的主题,然后我尝试编写自己的 class。 这是我写的:

namespace MyTool
{
public  class UpdaterTimer : Timer
{
    private static UpdaterTimer _timer;
    protected UpdaterTimer()
    { }

    public static UpdaterTimer GetTimer()
    {
        if (_timer == null)
            _timer = new UpdaterTimer();

        SetTimer(_timer);
        return _timer;
    }

    private static void SetTimer(UpdaterTimer _timer)
    {
        _timer.AutoReset = true;
        _timer.Interval = Utils.TimeBetweenChecksInMiliseconds;
        _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
        _timer.Start();
        DoStuff();
    }

    static void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        DoStuff();
    }

    private static void DoStuff()
    {
       //does stuff on each elapsed event occurrence
    }
}
}

简短的介绍:

  • 尝试使用 singleton 模式,因为我只需要一个计时器即可工作
  • 我不确定在 SetTimer() 方法中调用 DoStuff() ,这似乎是多余的。 但逻辑是,当应用程序启动时,DoStuff() 必须运行,然后它必须在每个 Timer.Elapsed 事件上再次运行。

我的问题是:

  1. 给定规范,您会以不同的方式编写此行为吗?
  2. 在这种情况下可以使用 singleton,还是没有意义?

我敢冒险,制作 Timer 的子类几乎没有用处。 你已经把大约 7 行代码变成了一大堆臃肿的东西。 鉴于您的DoStuff方法是私有的,因此它不会以任何方式重用。 因此,请考虑以下几行:

Action doStuff=()=>{
    //does stuff on each elapsed event occurance
};
_timer=new System.Timers.Timer(){
    AutoReset = true,
    Interval = Utils.TimeBetweenChecksInMiliseconds
};
_timer.Elapsed += (s,e)=>doStuff();
_timer.Start();
doStuff();

其中_timer是包含 class 的属性。 这里的意图很明确。 我认为它不值得拥有自己的 class。

编辑:

(s,e)=>doStuff()

是一个 lambda 表达式,它定义了一个接受两个参数se (发送者和事件)的委托。 由于这被添加到_timer.Elapsed事件(类型为ElapsedEventHandler ),编译器可以从事件类型ElapsedEventHandler推断类型se 我们的委托执行的唯一操作是doStuff()

简而言之,这可以扩展为:

_timer.Elapsed += delegate(object sender, ElapsedEventArgs e){doStuff();};

lambda 使我们可以更简洁地编写上述内容。

我认为你应该在这里使用组合而不是 inheritance。 (即不从 Timer 继承,只使用 Timer 作为私有字段)
此外,如果您之外的代码 class 不应更改计时器的属性,则不应将其显示给其他类。 如果您想为外部代码提供订阅计时器的Elapsed事件的能力,您可以为其添加一个事件并在您的DoStuff处理程序中触发它,如果没有 - 只需隐藏它。

OP 在评论中要求的代码示例:
注意:它符合规范(这是一个完全不同的用例,对于您的用例,您毕竟不需要单独的 class (请参阅另一个答案)),但显示了如何使用组合和隐藏实现细节。 (这个例子也有一些线程安全问题,但这不是重点。)

public static class TimerHelper
{
    private static Timer _timer;

    static TimerHelper()
    {
        _timer = new Timer(){AutoReset=true, Interval=1000};
        _timer.Start();
    }

    public static event ElapsedEventHandler Elapsed
    {
        add { _timer.Elapsed += value; } 
        remove { _timer.Elapsed -= value; }
    }
}

并使用:

// somewhere else in code
TimerHelper.Elapsed += new ElapsedEventHandler(TimerHelper_Elapsed);

我不知道您想要实现什么,但这里有几点关于您当前的设计:

  1. 您的GetTimer在多线程模式下损坏:

    if (_timer == null) _timer = new UpdaterTimer();

    假设您有两个线程,每个线程同时调用GetTimer ,第一个线程检查 _timer 并找到它 null 所以它继续。 但在那个时候和它到达_timer = new UpdateTimer()之前,线程上下文切换切换到另一个线程并暂停当前线程执行。 所以另一个线程检查_timer并发现它不是 null 所以它继续创建一个新的计时器,现在上下文切换重新安排了第一个线程并继续执行,因此创建一个新的计时器并更新旧的计时器。 正确使用 singleton 模式使用 static 构造函数代替static UpdaterTimer() { _timer = new UpdaterTimer();}

  2. 开发人员可以根据需要调用GetTimer() ,因此它将调用_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed); 再次注册另一个Elapsed处理程序。 另请注意,无论何时调用GetTimer ,计时器都会启动“ _timer.Start() ”,即使它已停止。

  3. 不要返回底层计时器,而是公开一个公共方法Start()Stop()UpdateInterval(int interval)

  4. SetTimer()你想立即调用DoStuff ,但是这会在SetTimer方法中被阻塞,等待DoStuff()完成,更好的方法是在新线程中启动该方法ThreadPool.QueueUserWorkItem(new WaitCallback((_) => DoStuff())); 或使用System.Threading.Timer而不是System.Timers.Timer并设置它立即调用方法启动。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM