Timer With Elapse Reentrance and Start Options October, 2009
I needed a timer that I could set to elapse immediately when it was enabled (Instead of waiting for the first elapse event) and also not be reentrant on the elapsed event. I couldn't seem to find anything out there so I rolled my own. Unfortunately the System.Timers.Timer doesn't offer a lot in the way of extension so I ended up just using composition over inheritance. Here is a quick test:
static Timer _timer = new Timer(5000, Timer.TimerElapseStartMode.Immediate, Timer.TimerElapseReentranceMode.NonReentrant); static void Main(string[] args) { Console.WriteLine( "Started @ {0}\r\n-----------------------------", DateTime.Now); _timer.Elapsed += _timer_Elapsed; _timer.Start(); Application.Run(); } static void _timer_Elapsed(object sender, Timer.ElapsedEventArgs e) { Console.WriteLine("Elapsed @ {0}", e.SignalTime); Thread.Sleep(10000); }
Here is the implementation:
public class Timer { // ────────────────────────── Enumerations ────────────────────────── public enum TimerElapseStartMode { Immediate, AfterInterval } public enum TimerElapseReentranceMode { Reentrant, NonReentrant } // ────────────────────────── Events ────────────────────────── public delegate void ElapsedEventHandler(object sender, ElapsedEventArgs e); public event ElapsedEventHandler Elapsed; // ────────────────────────── Private Fields ────────────────────────── private System.Timers.Timer _timer = new System.Timers.Timer(); private int _executing; // ────────────────────────── Constructor ────────────────────────── public Timer(double interval) : this( interval, TimerElapseStartMode.AfterInterval, TimerElapseReentranceMode.Reentrant) { } public Timer( double interval, TimerElapseStartMode startMode, TimerElapseReentranceMode reentranceMode) { _timer = new System.Timers.Timer(interval); _timer.Elapsed += OnElapsed; ElapseStartMode = startMode; ElapseReentranceMode = reentranceMode; } // ────────────────────────── Public Members ────────────────────────── public bool AutoReset { get { return _timer.AutoReset; } set { _timer.AutoReset = value; } } public bool Enabled { get { return _timer.Enabled; } set { _timer.Enabled = value; } } public double Interval { get { return _timer.Interval; } set { _timer.Interval = value; } } public TimerElapseStartMode ElapseStartMode { get; set; } public TimerElapseReentranceMode ElapseReentranceMode { get; set; } public void Start() { if (ElapseStartMode == TimerElapseStartMode.Immediate) ThreadPool.QueueUserWorkItem( state => Elapse(new ElapsedEventArgs())); Enabled = true; } public void BeginInit() { _timer.BeginInit(); } public void Close() { _timer.Close(); } public void EndInit() { _timer.EndInit(); } public void Stop() { Enabled = false; } // ────────────────────────── Private Members ────────────────────────── private void OnElapsed(object sender, System.Timers.ElapsedEventArgs e) { Elapse(new ElapsedEventArgs(e)); } private void Elapse(ElapsedEventArgs args) { if (ElapseReentranceMode == TimerElapseReentranceMode.NonReentrant && Interlocked.CompareExchange(ref _executing, 1, 0) == 1) return; if (Elapsed != null) Elapsed(this, args); _executing = 0; } // ────────────────────────── Nested Types ────────────────────────── public class ElapsedEventArgs : EventArgs { private DateTime _signalTime; public ElapsedEventArgs() { _signalTime = DateTime.Now; } public ElapsedEventArgs(System.Timers.ElapsedEventArgs args) { _signalTime = args.SignalTime; } public DateTime SignalTime { get { return _signalTime; } } } }