For original article, you might take a look here: http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
Timers
Comparing the Timer Classes in the .NET Framework Class Library
Alex Calvo
This article assumes you're familiar with C#
SUMMARY
Timers often play an important role in both client applications and server-based components (including Windows services). Writing effective timer-driven managed code requires a clear understanding of program flow and the subtleties of the .NET threading model. The .NET Framework Class Library provides three different timer classes: System.Windows.Forms.Timer, System.Timers.Timer, and System.Threading.Timer. Each of these classes has been designed and optimized for use in different situations. This article examines the three timer classes and helps you gain an understanding of how and when each class should be used.
Contents
Timer objects in Microsoft® Windows® allow you to control when actions take place. Some of the most common uses of timers are to start a process at a regularly scheduled time, to set intervals between events, and to maintain consistent animation speeds (regardless of processor speed) when working with graphics. In the past, timers have even allowed developers who use Visual Basic® to simulate multitasking.
As you would expect, the Microsoft .NET Framework arms you with the tools you need to tackle each of these scenarios. There are three different timer classes in the .NET Framework Class Library: System.Windows.Forms.Timer, System.Timers.Timer, and System.Threading.Timer. The first two classes appear in the Visual Studio® .NET toolbox window, allowing you to drag and drop both of these timer controls directly onto a Windows Forms designer or a component class designer. If you're not careful, this is where trouble can begin.
The Visual Studio .NET toolbox has a timer control on both the Windows Forms tab and the Components tab (see
Figure 1). It is very easy to use the wrong one, or worse yet, to not even realize that they are different. Use the timer control that is located under the Windows Forms tab only if the target is a Windows Forms designer. This control will place an instance of the System.Windows.Forms.Timer class on your form. Like all other controls in the toolbox, you can either let Visual Studio .NET handle the plumbing or you can manually instantiate and initialize the class yourself.
Figure 1
Timer Controls
The timer control that is located on the Components tab can be safely used in any class. This control creates an instance of the System.Timers.Timer class. If you're using the Visual Studio .NET toolbox, you can safely use this timer with either the Windows Forms designer or the component class designer. The component class designer is used by Visual Studio .NET when you're working on a class that derives from System.ComponentModel.Component (as is the case when you're working with Windows services). The System.Threading.Timer class does not appear on the Visual Studio .NET toolbox window. It is somewhat more complicated but provides a higher level of control, as you will see later in this article.
Figure 2
Sample Application
Let's first examine the System.Windows.Forms.Timer and System.Timers.Timer classes. These two classes have a very similar object model. Later I'll explore the more advanced System.Threading.Timer class.
Figure 2 shows a screenshot of the sample application that I will be referring to throughout this article. This application will help you gain a clear understanding of each of the timer classes. You can download the complete code from the link at the top of this article and experiment with it.
System.Windows.Forms.Timer
If you're looking for a metronome, you've come to the wrong place. The timer events raised by this timer class are synchronous with respect to the rest of the code in your Windows Forms app. This means that application code that is executing will never be preempted by an instance of this timer class (assuming you don't call Application.DoEvents). Just like the rest of the code in a typical Windows Forms application, any code that resides inside a timer event handler (for this type of timer class) is executed using the application's UI thread. During idle time, the UI thread is also responsible for processing all messages in the application's Windows message queue. This includes Windows API messages as well as the Tick events raised by this timer class. The UI thread processes these messages whenever your application isn't busy doing something else.
If you wrote Visual Basic code prior to Visual Studio .NET, you probably know that in a Windows-based application the only way to allow the UI thread to respond to Windows messages while executing in an event handler is to call the Application.DoEvents method. Just like with Visual Basic, calling Application.DoEvents from the .NET Framework can cause many problems. Application.DoEvents yields control to the UI message pump, allowing all pending events to be processed. This can alter the expected path of execution that I just mentioned. If Application.DoEvents is called from your code, your program flow may be interrupted in order to process the timer events generated by an instance of this class. This can cause unexpected behaviors and make debugging difficult.
How this timer class behaves becomes evident when running the sample application. Clicking the application's Start button, then its Sleep button, and finally the Stop button, will yield the following output:
System.Windows.Forms.Timer Started @ 4:09:28 PM
--> Timer Event 1 @ 4:09:29 PM on Thread: UIThread
--> Timer Event 2 @ 4:09:30 PM on Thread: UIThread
--> Timer Event 3 @ 4:09:31 PM on Thread: UIThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 4:09:36 PM on Thread: UIThread
System.Windows.Forms.Timer Stopped @ 4:09:37 PM
The sample application sets the Interval property of the System.Windows.Forms.Timer class to 1000 milliseconds. As you can see, if the timer event handler had continued to capture timer events while the main UI thread was sleeping (for five seconds), there would have been five timer events displayed as soon as the UI thread woke up again—one for each second the UI thread was sleeping. Instead, the timer remained in a suspended state while the UI thread was sleeping.
Programming the System.Windows.Forms.Timer class couldn't be easier—it has a very simple and intuitive programmatic interface. The Start and Stop methods essentially provide an alternate way of setting the Enabled property (which itself is a thin wrapper around Win32® SetTimer/ KillTimer functions). The Interval property, which I just mentioned, is self-explanatory. Even though you can technically set the Interval property as low as one millisecond, you should be aware that the .NET Framework documentation states that this property is only accurate to approximately 55 milliseconds (provided that the UI thread is available for processing).
Capturing the events raised by an instance of the System.Windows.Forms.Timer class is handled by wiring the Tick event to a standard EventHandler delegate, as shown by the code snippet in the following example:
System.Windows.Forms.Timer tmrWindowsFormsTimer = new
System.Windows.Forms.Timer();
tmrWindowsFormsTimer.Interval = 1000;
tmrWindowsFormsTimer.Tick += new
EventHandler(tmrWindowsFormsTimer_Tick);
tmrWindowsFormsTimer.Start();
•••
private void tmrWindowsFormsTimer_Tick(object sender,
System.EventArgs e) {
//Do something on the UI thread...
}
System.Timers.Timer
The .NET Framework documentation refers to the System.Timers.Timer class as a server-based timer that was designed and optimized for use in multithreaded environments. Instances of this timer class can be safely accessed from multiple threads. Unlike the System.Windows.Forms.Timer, the System.Timers.Timer class will, by default, call your timer event handler on a worker thread obtained from the common language runtime (CLR) thread pool. This means that the code inside your Elapsed event handler must conform to a golden rule of Win32 programming: an instance of a control should never be accessed from any thread other than the thread that was used to instantiate it.
The System.Timers.Timer class provides an easy way to deal with this dilemma—it exposes a public SynchronizingObject property. Setting this property to an instance of a Windows Form (or a control on a Windows Form) will ensure that the code in your Elapsed event handler runs on the same thread on which the SynchronizingObject was instantiated.
If you use the Visual Studio .NET toolbox, Visual Studio .NET will automatically set the SynchronizingObject property to the current form instance. At first it may seem as though using this timer class with the SynchronizingObject property makes it functionally equivalent to using the System.Windows.Forms.Timer. For the most part, it is. When the operating system notifies the System.Timers.Timer class that the enabled timer has elapsed, the timer uses the SynchronizingObject.Begin.Invoke method to execute the Elapsed event delegate on the thread on which the SynchronizingObject's underlying handle was created. The event handler will be blocked until the UI thread is able to process it. However, unlike with System.Windows.Forms.Timer, the event will still eventually be raised. As you saw in
Figure 2, 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.
Figure 3 shows an example of how to use the SynchronizingObject property. You can use the sample application to analyze this class by selecting the System.Timers.Timer radio button and running through the same sequence of events as you did with the System.Windows.Forms.Timer. Doing so will produce the output shown in
Figure 4.
Figure 4 Output
System.Timers.Timer Started @ 5:15:01 PM
--> Timer Event 1 @ 5:15:02 PM on Thread: WorkerThread
--> Timer Event 2 @ 5:15:03 PM on Thread: WorkerThread
--> Timer Event 3 @ 5:15:04 PM on Thread: WorkerThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 5:15:05 PM on Thread: WorkerThread
--> Timer Event 5 @ 5:15:06 PM on Thread: WorkerThread
--> Timer Event 6 @ 5:15:07 PM on Thread: WorkerThread
--> Timer Event 7 @ 5:15:08 PM on Thread: WorkerThread
--> Timer Event 8 @ 5:15:09 PM on Thread: WorkerThread
System.Timers.Timer Stopped @ 5:15:10 PM
Figure 3 Using the SynchronizingObject Property
System.Timers.Timer tmrTimersTimer = new System.Timers.Timer();
tmrTimersTimer.Interval = 1000;
tmrTimersTimer.Elapsed += new
ElapsedEventHandler(tmrTimersTimer_Elapsed);
tmrTimersTimer.SynchronizingObject = this; //Synchronize with
//the current form...
tmrTimersTimer.Start();
•••
private void tmrTimersTimer_Elapsed(object sender,
System.Timers.ElapsedEventArgs e) {
// Do something on the UI thread (same thread the form was
// created on)...
// If we didn't set SynchronizingObject we would be on a
// worker thread...
}
As you can see, it didn't skip a beat—even when the UI thread was sleeping. An Elapsed event handler was queued to be executed at each event interval. Because the UI thread was sleeping, however, the sample application displays the five timer events (4 through 8) all at once when the UI thread wakes up again and is able to process the handlers.
As I mentioned earlier, the System.Timers.Timer class members are very similar to those of the System.Windows.Forms.Timer class. The biggest difference is that System.Timers.Timer is a wrapper around Win32 waitable timer objects and raises an Elapsed event on a worker thread rather than a Tick event on the UI thread. The Elapsed event must be connected to an event handler that matches the ElapsedEventHandler delegate. The event handler receives an argument of type ElapsedEventArgs.
Above and beyond the standard EventArgs members, the ElapsedEventArgs class exposes a public SignalTime property, which contains the exact time the timer elapsed. Because this class supports access from different threads, it is conceivable that the Stop method may be called on a thread other than the thread that is used for the Elapsed event. This could potentially result in the Elapsed event firing even after the Stop method has been called. You can deal with this by comparing the SignalTime property to the time the Stop method was called.
The System.Timers.Timer class also provides an AutoReset property that determines if the Elapsed event should fire continuously or just once. Keep in mind that resetting the Interval property after the timer has started will reset the current count back to zero. For example, if the interval is set to 5 seconds and 3 seconds have already elapsed before the interval is changed to 10 seconds, the next timer event will be 13 seconds from the last timer event.
System.Threading.Timer
The third timer class comes from the System.Threading namespace. I'd like to say that this is the best of all timer classes, but that would be misleading. For one thing, I was surprised to find that instances of this class are not inherently thread safe, given that it resides in the System.Threading namespace. (Obviously, this doesn't mean it can't be used in a thread-safe manner.) The programmatic interface of this class is not consistent with the other two timer classes and it's also a bit more cumbersome.
Unlike the previous two timer classes that I have just covered, System.Threading.Timer has four overloaded constructors. The following shows what they look like:
public Timer(TimerCallback callback, object state, long dueTime,
long period);
public Timer(TimerCallback callback, object state, UInt32 dueTime,
UInt32 period);
public Timer(TimerCallback callback, object state, int dueTime,
int period);
public Timer(TimerCallback callback, object state, TimeSpan dueTime,
TimeSpan period);
The first parameter (callback) requires a TimerCallback delegate that points to a method with the following signature:
public void TimerCallback(object state);
The second parameter (state) can either be null or an object containing application-specific information. This state object is passed to your timer callback function during each timer event invocation. Keep in mind that the timer callback function is executed on a worker thread, so you'll want to ensure that you have thread-safe access to the state object.
The third parameter (dueTime) allows you to specify when the initial timer event should be fired. You can specify 0 to start the timer immediately or to prevent the timer from automatically starting, you can use the System.Threading.Timeout.Infinite constant.
The fourth parameter (period) allows you to specify the interval (in milliseconds) at which the callback function should be called. Specifying either 0 or Timeout.Infinite for this parameter will disable subsequent timer event invocations.
Once the constructor has been called, you can still alter the dueTime and period settings by using the Change method. This method has the following four overloads:
public bool Change(int dueTime, int period);
public bool Change(uint dueTime, uint period);
public bool Change(long dueTime, long period);
public bool Change(TimeSpan dueTime, TimeSpan period);
Here's the code I used in the sample application to start and stop this timer:
//Initialize the timer to not start automatically...
System.Threading.Timer tmrThreadingTimer = new
System.Threading.Timer(new
TimerCallback(tmrThreadingTimer_TimerCallback),
null, System.Threading.Timeout.Infinite, 1000);
//Manually start the timer...
tmrThreadingTimer.Change(0, 1000);
//Manually stop the timer...
tmrThreadingTimer.Change(Timeout.Infinite, Timeout.Infinite);
As you might expect, running the sample application with the System.Threading.Timer class selected results in the same output you just saw with the System.Timers.Timer class. Because the TimerCallback function gets called on a worker thread, there are no skipped beats (assuming the availability of worker threads).
Figure 5 shows the output from the sample application.
Figure 5 Sample App Output
System.Threading.Timer Started @ 7:17:11 AM
--> Timer Event 1 @ 7:17:12 AM on Thread: WorkerThread
--> Timer Event 2 @ 7:17:13 AM on Thread: WorkerThread
--> Timer Event 3 @ 7:17:14 AM on Thread: WorkerThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 7:17:15 AM on Thread: WorkerThread
--> Timer Event 5 @ 7:17:16 AM on Thread: WorkerThread
--> Timer Event 6 @ 7:17:17 AM on Thread: WorkerThread
--> Timer Event 7 @ 7:17:18 AM on Thread: WorkerThread
--> Timer Event 8 @ 7:17:19 AM on Thread: WorkerThread
System.Threading.Timer Stopped @ 7:17:20 AM
Unlike the System.Timers.Timer class, there is no counterpart to the SynchronizingObject property that was offered by the System.Timers.Timer class. Any operations that require access to UI controls should be marshaled using the Control's Invoke or BeginInvoke methods.
Thread-safe Programming with Timers
To maximize code reuse, the sample application calls the same ShowTimerEventFired method from all three different types of timer events. Here are the three timer event handlers:
private void tmrWindowsFormsTimer_Tick(object sender,
System.EventArgs e) {
ShowTimerEventFired(DateTime.Now, GetThreadName());
}
private void tmrTimersTimer_Elapsed(object sender,
System.Timers.ElapsedEventArgs e) {
ShowTimerEventFired(DateTime.Now, GetThreadName());
}
private void tmrThreadingTimer_TimerCallback(object state) {
ShowTimerEventFired(DateTime.Now, GetThreadName());
}
As you can see, the ShowTimerEventFired method takes the current time and current thread name as arguments. In order to distinguish worker threads from the UI thread, the main entry point for the sample application sets the Name property of the CurrentThread object to "UIThread." The GetThreadName helper method returns either the value of Thread.CurrentThread.Name or "WorkerThread" if the Thread.CurrentThread.IsThreadPoolThread property is true.
Because the timer events for System.Timers.Timer and System.Threading.Timer execute on worker threads, it is imperative that any user interface code within these event handlers be marshaled back onto the UI thread for processing. To do this, I created a delegate called ShowTimerEventFiredDelegate:
private delegate void
ShowTimerEventFiredDelegate
(DateTime eventTime,
string threadName);
ShowTimerEventFiredDelegate allows the ShowTimerEventFired method to call itself back on the UI thread.
Figure 6 shows the code that makes all this happen.
Figure 6 ShowTimerEventFired
private void ShowTimerEventFired(DateTime eventTime,
string threadName) {
//InvokeRequired will be true when using
//System.Threading.Timer or System.Timers.Timer (without a
//SynchronizationObject)...
if (lstTimerEvents.InvokeRequired) {
//Marshal this call back to the UI thread (via the form
//instance)...
BeginInvoke(new
ShowTimerEventFiredDelegate(ShowTimerEventFired),
new object[] {eventTime, threadName});
}
else
lstTimerEvents.TopIndex = lstTimerEvents.Items.Add(
String.Format("—> Timer Event {0} @ {1} on Thread:
{2}",
++_tickEventCounter, eventTime.ToLongTimeString(),
threadName));
}
It's very easy to determine whether you can safely access a Windows Forms control from the current thread by querying its InvokeRequired property. In this example, if the ListBox's InvokeRequired property is true, the form's BeginInvoke method can be used to call the ShowTimerEventFired method again via ShowTimerEventFiredDelegate. This will ensure that the ListBox Add method executes on the UI thread.
As you can see, there are many issues you need to be aware of when programming with asynchronous timer events. I recommend that you read Ian Griffith's article "
Windows Forms: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads," from the February 2003 issue of
MSDN Magazine before using either System.Timers.Timer or System.Threading.Timer.
Dealing with Timer Event Reentrance
There's another subtle issue you'll need to consider when working with asynchronous timer events, such as the events generated by System.Timers.Timer and System.Threading.Timer. The problem has to do with code reentrance. If the code in your timer event handler takes longer to execute than the interval at which the timer is raising events, and you haven't taken the necessary precautions to guard against multithreaded access to your objects and variables, then you could be in for some difficult debugging sessions. Take a look at the following code snippet:
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());
}
Assuming your timer Interval property is set to 1000 milliseconds, you may be surprised to find out that the first message box that pops up will show a value of 5. This is because during the five seconds that the first timer event was sleeping, the timer kept on generating Elapsed events on different worker threads. Hence, the value of the tickCounter variable was incremented five times before the processing of the first timer event was completed. Notice how I used the Interlocked.Increment method to increment the tickCounter variable in a thread-safe manner. There are other ways to do this, but the Interlocked.Increment method was specifically designed for this kind of operation.
One easy way to solve this type of reentrance problem is to sandwich your timer event handler in a block of code that temporarily disables and then reenables the timer, as shown in the following example:
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;
}
With this code in place, message boxes will show up every five seconds and, as you would expect, the value of tickCounter will be incremented by one each time. Another option would be to use a synchronization primitive such as Monitor or a mutex to ensure that all future events are queued until the current handler has finished executing.
Conclusion
For a quick recap of my look at the three timer classes available in the .NET Framework, see the table in
Figure 7 which compares these three classes. A point you may want to consider when working with timers is whether your problem can be solved more simply by using the Windows Scheduler (or the AT command for that matter) to run a standard executable periodically.
Figure 7 Timer Classes in the .NET FCL
System.Windows.Forms System.Timers System.Threading
Timer event runs on what thread? |
UI thread |
UI or worker thread |
Worker thread |
Instances are thread safe? |
No |
Yes |
No |
Familiar/intuitive object model? |
Yes |
Yes |
No |
Requires Windows Forms? |
Yes |
No |
No |
Metronome-quality beat? |
No |
Yes* |
Yes* |
Timer event supports state object? |
No |
No |
Yes |
Initial timer event can be scheduled? |
No |
No |
Yes |
Class supports inheritance? |
Yes |
Yes |
No |
* Depending on the availability of system resources (for example, worker threads) |
For background information see:
Programming the Thread Pool in the .NET Framework: Using Timers
.NET Framework Class Library