Search code examples
multithreadingc#-4.0timer

Disabling a System.Threading.Timer instance while its callback is in progress


I am using two instances of System.Threading.Timer to fire off 2 tasks that are repeated periodically.

My question is: If the timer is disabled but at that point of time this timer is executing its callback on a thread, then will the Main method exit, or will it wait for the executing callbacks to complete?

In the code below, Method1RunCount is synchronized for read and write using lock statement ( this part of code is not shown below). The call back for timer1 increments Method1RunCount by 1 at end of each run.

static void Main(string[] args)
{
    TimerCallback callback1 = Method1;
    System.Threading.Timer timer1 = new System.Threading.Timer(callback1,null,0, 90000);
    TimerCallback callback2 = Method2;
    System.Threading.Timer timer2 = new System.Threading.Timer(callback2, null, 0, 60000);
    while (true)
    {
         System.Threading.Thread.Sleep(250);
         if (Method1RunCount == 4)
         {
              //DISABLE the  TIMERS
              timer1.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
              timer2.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
              break;
         }
     }
 }

Solution

  • This kind of code tends to work by accident, the period of the timer is large enough to avoid the threading race on the Method1RunCount variable. Make the period smaller and there's a real danger that the main thread won't see the value "4" at all. Odds go down considerably when the processor is heavily loaded and the main thread doesn't get scheduled for while. The timer's callback can then execute more than once while the main thread is waiting for the processor. Completing missing the value getting incremented to 4. Note how the lock statement does not in fact prevent this, it isn't locked by the main thread since it is probably sleeping.

    There's also no reasonable guess you can make at how often Method2 runs. Not just because it has a completely different timer period but fundamentally because it isn't synchronized to either the Method1 or the Main method execution at all.

    You'd normally increment Method1RunCount at the end of Method1. That doesn't otherwise guarantee that Method1 won't be aborted. It runs on a threadpool thread, they have the Thread.IsBackground property always set to true. So the CLR will readily abort them when the main thread exits. This again tends to not cause a problem by accident.

    If it is absolutely essential that Method1 executes exactly 4 times then the simple way to ensure that is to let Method1 do the counting. Calling Timer.Change() inside the method is fine. Use a class like AutoResetEvent to let the main thread know about it. Which now no longer needs the Sleep anymore. You still need a lock to ensure that Method1 cannot be re-entered while it is executing. A good way to know that you are getting thread synchronization wrong is when you see yourself using Thread.Sleep().