Timers Timers Everywhere – The problem of plenty with .NET Timers

Hello World,

When it comes to scenarios in your .NET projects where you need to do some work periodically, the natural solution is to use a Timer that fires events periodically and then register the work to be done in the form of an event-handler or callback method.

.NET offers several flavors (or varieties or types – take your pick!) of Timer classes, each suited to a particular context. This post is intended to enlist and elucidate these Timer types and provide suggestions on when to choose one over the other.

And The Options are…

There are 4 classes named Timer in .NET:

  1. System.Timers.Timer – (Living under the System.ComponentModel.Component namespace) Fires an event and executes code [on the event handler] at regular intervals. This one is intended to be used as a sever-based or service component in a multithreaded environment.
  2. System.Threading.Timer – (Living under the System.Threading namespace) – Executes a single callback on a thread pool thread and is also intended to be used in a server-based or service component in a multithreaded environment. The callback method can only be assigned on instantiation and cannot be changed afterwards.
  3. System.Windows.Forms.Timer – (Living under the System.Windows.Forms namespace) – Fires and event and executes code in one or more event handlers at regular intervals and is intended to be used in a single threaded environment
  4. System.Web.UI.Timer  (Living under the System.Web.UI namespace) – Is an ASP.NET component intended to be used to perform asynchronous or synchronous web page postbacks at regular intervals (that stuff is antique, isn’t it? with all these brilliant client-side JavaScript frameworks like jQuery around – so we will not hear anymore about this guy 🙂 )

Lets take a deep breath

So now then, it is clear that the System.Windows.Forms.Timer is only intended for use with a WinForms (or thick-client) application, but what’s with the System.Timers.Timer and System.Threading.Timer guys – both claiming to offer server-side multithreaded capabilities? Let’s dive in and find out.

System.Threading.Timer

The most commonly used way to create an instance of this timer is:

public Timer(
	TimerCallback callback,
	Object state,
	int dueTime,
	int period
)
  • This Timer invokes the callbacks on worker threads drawn from the CLR thread pool. Note that the callbacks can only be registered when an instance of the Timer class is created
  • It does not offer a SynchronizationObject property or a similar construct. What that means is that if you need to use this in a Windows application, you would need to marshal any operations that require access to the UI thread using the control’s Invoke OR BeginInvoke.
  • It is trivial to change the frequency of firing the callback event by calling one of the overloads of the Change method: e.g.
    timerInstance.Change(dueTime, period);

    Here, dueTime is the interval after which the callback would first be called while period is the regular interval at which the callback would be called.

  • The callback invoked by this timer is of the type of TimerCallback delegate and has the following signature:
    public void TimerCallback(object state);

    If you do not need to pass up anything to the callback, you can safely pass a null to the state parameter.

System.Timers.Timer

The two ways to create an instance of this Timer class are:

//1. Create and then set interval
var timer = new Timer();
timer.Interval = 1000; //milliseconds
timer.Elapsed += OnTimerElapsed;

//2. Create with an initial interval</pre>
var timer1 = new Timer(2000); //milliseconds
timer1.Elapsed += OnTimerElapsed;

// Timer elapsed event handler
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
     // Do something useful
}
  • Conceptually, this Timer class wraps the System.Threading.Timer and provides features like dispatching on a particular thread – or in more precise words, it offers the capability to set a Synchronization Object. This is achieved by setting its public SynchronizationObject property that would allow the callback to be called on a specific thread (most likely the UI Thread).
  • As with the Threading.Timer class, the callback is invoked on a CLR thread-pool thread.
  • When used via the design surface – for instance on a WinForms application, the SynchronizationObject is automatically set to the current form instance
  • A major difference between using this timer in a WinForms scenario is that System.Windows.Forms.Timer will not raise events that occur while the UI thread is unable to process them, whereas System.Timers.Timer will queue them to be processes when the UI thread is available.

From the above two excerpts, it is evident that the choice of which Timer class you use would depend on the following major factors:

  1. Whether you use case runs in a single-threaded or multi-threaded environment
  2. Do you need access to a UI thread OR want the callback to run code on a specific thread? (or simply put, do you care which thread executes the code attached to the event-handler called by the timer)

Effectively, if in a WinForms application, you have to take your pick between System.Windows.Forms.Timer and System.Timers.Timer. The decision then would depend on whether you care if some events are missed (as is the case with Forms.Timer) or want each and every elapsed event to be processed.

Some Caveats

  • As both System.Threading.Timer and System.Timers.Timer provide multithreaded capabilities, code that uses them must account for thread safety or more precisely, handle re-entrancy. This is so because the event handler running the code on an elapsed event will be invoked event if the previous invocation hasn’t yet completed. Quoting an example from the MSDN magazine, consider the following case:
private int tickCounter = 0;
private void tmrTimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
   System.Threading.Interlocked.Increment(ref tickCounter); Thread.Sleep(5000);             MessageBox.Show(tickCounter.ToString());
}

Now if you set the interval to say 1000, you’d see the first message box show a value of 5! The easy fix is to disable the timer inside the elapsed event handler and then re-enable it once you’re done with the work:

private void tmrTimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
tmrTimersTimer.Enabled = false;
System.Threading.Interlocked.Increment(ref tickCounter);
Thread.Sleep(5000);
MessageBox.Show(tickCounter.ToString());
tmrTimersTimer.Enabled = true;
}
  • Another thing to note is that the precision of the interval in case of System.Timer.Timer interval can only be Int32 value while System.Threading.Timer interval can be up to Int64. This then becomes another decision point when using a timer server side.
  • A Timer uses system resources, so you must always remember to be a good citizen in the .NET world and Dispose off the instances
  • Always keep a reference to the timer instance, lest you stand the risk of getting an ObjectDisposed exception as your timer instance may have been garbage collected (even if the Timer is active)

Some Alternatives

A viable alternative with community support for your scheduling needs is the wonderful Quartz.net enterprise scheduler. You can find more information on it HERE. This library is a port of the Java Quartz one and is actively developed by Marko Lahma. I’ve had the chance to use it and can vouch that it does do what it promises (precision scheduling of jobs) and does it very well.

References:

Until next time,

Happy Coding!

Advertisements
Tagged with: ,
Posted in .NET, C#

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

  • Comic for March 28, 2017
    Dilbert readers - Please visit Dilbert.com to read this feature. Due to changes with our feeds, we are now making this RSS feed a link to Dilbert.com.
%d bloggers like this: