Wednesday, August 13, 2008

Timers: executing a task at regular intervals

When you want to execute a piece of code at regular intervals, you should not start a dedicated thread to do this. Instead you should use a timer.

If the task you want to perform involves GUI interaction, it is best to use the Windows.Forms.Timer class. An example:

   1: class Test
   2: {
   3:   System.Windows.Forms.Timer m_Timer = new System.Windows.Forms.Timer();
   4:  
   5:   public void StartTimer()
   6:   {
   7:     m_Timer.Interval = 1000;    // tick every 1000 ms
   8:     m_Timer.Tick += Timer_Tick;
   9:     m_Timer.Start();
  10:   }
  11:  
  12:   void Timer_Tick(object sender, EventArgs e)
  13:   {
  14:     // This code will execute every second on the GUI thread
  15:   }
  16: }

If the code does not involve GUI interaction, it is best to use a threadpool-timer. However you need to be careful: if your code takes longer to execute than the interval of the timer, you will cause threadpool starvation! This occurs if the maximum amount of threadpool-threads is reached. This will cause serious problems for the complete process because all code that uses the threadpool (including .NET Remoting) will be blocked.

As a workaround you can set the timer to execute only once, and reset it every time it has executed:

   1: class Test
   2: {
   3:   System.Threading.Timer m_Timer;
   4:  
   5:   public void StartTimer()
   6:   {
   7:     // set timer to be executed only once
   8:     m_Timer = new System.Threading.Timer(Timer_Tick,null,1000,0);
   9:   }
  10:  
  11:   void Timer_Tick(object state)
  12:   {
  13:     try
  14:     {
  15:       // This code will execute every second on a threadpool thread
  16:     }
  17:     finally
  18:     {
  19:       // reset the timer
  20:       m_Timer.Change(1000, 0);
  21:     }
  22:   }
  23: }

So there is no need to start a dedicated thread just to perform a piece of code at regular intervals.

9 comments:

WWallace said...

Where do you put the Timer.Dispose() in such a way that the final iteration of the repeated code does not get truncated? Or is that just an imaginary problem that doesn't happen?

Kristof said...

@davidinnz: very good question, indeed you should not forget to dispose the timer.

Where you dispose the timer will depend of what you're trying to achieve of course. If you dispose the timer within the Timer_Tick method, the method will not be executed again. If you dispose the timer from somewhere else (from another thread), you cannot be sure if the method will be executed again. There is a small chance that, even though the timer is now disposed, the Timer_Tick method was already scheduled to execute on the threadpool.
Note that if the timer is disposed while the Timer_Tick method is executing, you can be sure that it will be allowed to executed until it is finished. So you need not worry that the code will be 'truncated' as you say.

Kadir Kılıçoğlu said...

It is said that both timer classes (the Windows.Forms and the System.Threading ones) do not execute precisely.

I mean, for example, the programmer sets the timer so that some job is executed once a minute but in real, the timer behaves unpredictably and the job is executed in, let's say, {62, 60, 61, 68 ...} seconds.

Why do both timers act so weird and is there a proper solution or workaround to solve this.

Also do you recommend both timers for time-important algorithms. If not, have you any recommandations?

And one last question: is the Win32 timer better than these timers?

Thank you very much.

Kristof said...

@Kadir:

Windows is not a realtime OS, so it cannot guarantee that your timer will be very precise. If the system is very busy when your timer executes, it will indeed not be very precise. It all depends on the precision that you really need. For most cases both timers will be precise enough, but that might not be the case in your situation.
Generally the Windows.Forms timer will be less precise, because it will not execute if the GUI thread is blocked.
If you really need a high precision and the timer interval is very low, you could use 'busy waiting' (see wikipedia). Also, I think that in kernel mode there are other mechanisms to get a high precision, but that implies that you write a driver (which cannot be done in C#).

Kristof said...

Thx for the advise Kristof,

Is there a way to keep the GUI thread waiting on the worker thread that is started by the (System.Threading.)Timer? This would be helpful for example when shutting down the application, to avoid a ThreadAbortException. I usually make use of the Thread.Join()-method, but in this case I have no Thread-instance to work with, I guess :-)

Kristof said...

@Kristof: if you want the GUI-thread to wait until the timer has been executed, you can use a ManualResetEvent for this. You can wait on this in the GUI thread, and set it from your timer's eventhandler.
Note however that you GUI thread will now be blocked until the timer executes - depending on your situation this may not be what you want.

Jayson Ragasa said...

I tried calling Dispose because I want to Stop the timer but its throwing an error "Cannot access a disposed object." in finally{} block

Jayson Ragasa said...

I just did it this way

finally
{
try
{
t.Change(Interval, 0);
}
catch { }
}

Unknown said...

What if in StartTimer I do not want any code to execute till my polling(Timer_Tick) gets the response I need? So basically the code keeps executing at set interval and once it gets the result it need only then any other code is executed.