I would like to know if the code I wrote is a properly written one, it does function, but I've done bad designs before, so I need to know if I thought this in a proper way.
Code is about using a System.Timers.Timer to do a repeated action each X hours. I read some threads about this subject on stackoverflow and then I tried writing my own class. Here is what I wrote:
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
}
}
}
Short description:
My questions are:
I don't know what you are trying to achieve but here is a few point about your current design:
Your GetTimer
is broken in multithreading mode:
if (_timer == null)
_timer = new UpdaterTimer();
suppose you have two threads each one call GetTimer
at the same time, the first one checks the _timer and found it null so it proceed. but at that time and before it reach _timer = new UpdateTimer()
the thread context switching switch to the other thread and pause the current thread execution. so the other thread check the _timer
and find it not null so it proceed and create a new timer, now the context switch rescheduled the first thread and its continue its execution and so create a new timer and update the old one. to correct use of singleton pattern use static constructor instead static UpdaterTimer() { _timer = new UpdaterTimer();}
The developer can call GetTimer()
as much as he wanted so it will call _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
again registering another Elapsed
handler. Also note that whenever you call GetTimer
, the timer will start "_timer.Start()
" even if it were stopped.
Don't return the underlying timer, instead expose a public methods Start()
, Stop()
, UpdateInterval(int interval)
.
At SetTimer()
you want to call DoStuff
immediately, but that then will be blocked at the SetTimer
method waiting DoStuff()
to complete, a better way is to start that method in a new thread ThreadPool.QueueUserWorkItem(new WaitCallback((_) => DoStuff()));
or use System.Threading.Timer instead of System.Timers.Timer
and set it call the method start immediately.