[英]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
}
}
}
简短的介绍:
我的问题是:
我敢冒险,制作 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 表达式,它定义了一个接受两个参数s
和e
(发送者和事件)的委托。 由于这被添加到_timer.Elapsed
事件(类型为ElapsedEventHandler
),编译器可以从事件类型ElapsedEventHandler
推断类型s
和e
。 我们的委托执行的唯一操作是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);
我不知道您想要实现什么,但这里有几点关于您当前的设计:
您的GetTimer
在多线程模式下损坏:
if (_timer == null) _timer = new UpdaterTimer();
假设您有两个线程,每个线程同时调用GetTimer
,第一个线程检查 _timer 并找到它 null 所以它继续。 但在那个时候和它到达_timer = new UpdateTimer()
之前,线程上下文切换切换到另一个线程并暂停当前线程执行。 所以另一个线程检查_timer
并发现它不是 null 所以它继续创建一个新的计时器,现在上下文切换重新安排了第一个线程并继续执行,因此创建一个新的计时器并更新旧的计时器。 正确使用 singleton 模式使用 static 构造函数代替static UpdaterTimer() { _timer = new UpdaterTimer();}
开发人员可以根据需要调用GetTimer()
,因此它将调用_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
再次注册另一个Elapsed
处理程序。 另请注意,无论何时调用GetTimer
,计时器都会启动“ _timer.Start()
”,即使它已停止。
不要返回底层计时器,而是公开一个公共方法Start()
、 Stop()
、 UpdateInterval(int interval)
。
在SetTimer()
你想立即调用DoStuff
,但是这会在SetTimer
方法中被阻塞,等待DoStuff()
完成,更好的方法是在新线程中启动该方法ThreadPool.QueueUserWorkItem(new WaitCallback((_) => DoStuff()));
或使用System.Threading.Timer而不是System.Timers.Timer
并设置它立即调用方法启动。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.