Tutorial :Synchronizing a timer to prevent overlap



Question:

I'm writing a Windows service that runs a variable length activity at intervals (a database scan and update). I need this task to run frequently, but the code to handle isn't safe to run multiple times concurrently.

How can I most simply set up a timer to run the task every 30 seconds while never overlapping executions? (I'm assuming System.Threading.Timer is the correct timer for this job, but could be mistaken).


Solution:1

You could do it with a Timer, but you would need to have some form of locking on your database scan and update. A simple lock to synchronize may be enough to prevent multiple runs from occurring.

That being said, it might be better to start a timer AFTER you're operation is complete, and just use it one time, then stop it. Restart it after your next operation. This would give you 30 seconds (or N seconds) between events, with no chance of overlaps, and no locking.

Example :

System.Threading.Timer timer = null;    timer = new System.Threading.Timer((g) =>    {        Console.WriteLine(1); //do whatever          timer.Change(5000, Timeout.Infinite);    }, null, 0, Timeout.Infinite);  

Work immediately .....Finish...wait 5 sec....Work immediately .....Finish...wait 5 sec....


Solution:2

I'd use Monitor.TryEnter in your elapsed code:

if (Monitor.TryEnter(lockobj))  {    try    {      // we got the lock, do your work    }    finally    {       Monitor.Exit(lockobj);    }  }  else  {    // another elapsed has the lock  }  


Solution:3

I prefer System.Threading.Timer for things like this, because I don't have to go through the event handling mechanism:

Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, 30000);    object updateLock = new object();  void UpdateCallback(object state)  {      if (Monitor.TryEnter(updateLock))      {          try          {              // do stuff here          }          finally          {              Monitor.Exit(updateLock);          }      }      else      {          // previous timer tick took too long.          // so do nothing this time through.      }  }  

You can eliminate the need for the lock by making the timer a one-shot and re-starting it after every update:

// Initialize timer as a one-shot  Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, Timeout.Infinite);    void UpdateCallback(object state)  {      // do stuff here      // re-enable the timer      UpdateTimer.Change(30000, Timeout.Infinite);  }  


Solution:4

instead of locking (which could cause all of your timed scans to wait and eventually stack up). You could start the scan/update in a thread and then just do a check to see if the thread is still alive.

Thread updateDBThread = new Thread(MyUpdateMethod);  

...

private void timer_Elapsed(object sender, ElapsedEventArgs e)  {      if(!updateDBThread.IsAlive)          updateDBThread.Start();  }  


Solution:5

You could use the AutoResetEvent as follows:

// Somewhere else in the code  using System;  using System.Threading;    // In the class or whever appropriate  static AutoResetEvent autoEvent = new AutoResetEvent(false);    void MyWorkerThread()  {     while(1)     {       // Wait for work method to signal.          if(autoEvent.WaitOne(30000, false))          {              // Signalled time to quit              return;          }          else          {              // grab a lock              // do the work              // Whatever...          }     }  }  

A slightly "smarter" solution is as follow in pseudo-code:

using System;  using System.Diagnostics;  using System.Threading;    // In the class or whever appropriate  static AutoResetEvent autoEvent = new AutoResetEvent(false);    void MyWorkerThread()  {    Stopwatch stopWatch = new Stopwatch();    TimeSpan Second30 = new TimeSpan(0,0,30);    TimeSpan SecondsZero = new TimeSpan(0);    TimeSpan waitTime = Second30 - SecondsZero;    TimeSpan interval;      while(1)    {      // Wait for work method to signal.      if(autoEvent.WaitOne(waitTime, false))      {          // Signalled time to quit          return;      }      else      {          stopWatch.Start();          // grab a lock          // do the work          // Whatever...          stopwatch.stop();          interval = stopwatch.Elapsed;          if (interval < Seconds30)          {             waitTime = Seconds30 - interval;          }          else          {             waitTime = SecondsZero;          }       }     }   }  

Either of these has the advantage that you can shutdown the thread, just by signaling the event.


Edit

I should add, that this code makes the assumption that you only have one of these MyWorkerThreads() running, otherwise they would run concurrently.


Solution:6

I've used a mutex when I've wanted single execution:

    private void OnMsgTimer(object sender, ElapsedEventArgs args)      {          // mutex creates a single instance in this application          bool wasMutexCreatedNew = false;          using(Mutex onlyOne = new Mutex(true, GetMutexName(), out wasMutexCreatedNew))          {              if (wasMutexCreatedNew)              {                  try                  {                        //<your code here>                  }                  finally                  {                      onlyOne.ReleaseMutex();                  }              }          }        }  

Sorry I'm so late...You will need to provide the mutex name as part of the GetMutexName() method call.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »