How to let Timer skip tick if the previous thread is still busy

19,302

Solution 1

You might try disabling the timer during processing, something like

// Just in case someone wants to inherit your class and lock it as well ...
private static object _padlock = new object();
try
{
  serviceTimer.Stop(); 

  lock (_padlock)
    { 
        // do some heavy processing... 
    } 
}
finally
{
  serviceTimer.Start(); 
}

Edit : OP didn't specify whether the reentrancy was caused only by the timer or whether the service was multi threaded. Have assumed the later, but if the former then locking should be unnecessary if the timer is stopped (AutoReset or manually)

Solution 2

You don't need the lock in this case. Set timer.AutoReset=false before starting it. Restart the timer in the handler after you are done with your processing. This will ensure that the timer fires 60 seconds after each task.

Solution 3

A similar variation on other answers, that allows the timer to keep ticking and only do the work when the lock can be obtained, instead of stopping the timer.

Put this in the elapsed event handler:

if (Monitor.TryEnter(locker)
{
    try
    {
        // Do your work here.
    }
    finally
    {
        Monitor.Exit(locker);
    }
}

Solution 4

Put a quick check it see if the service is running. if it is running it will skip this event and wait for the next one to fire.

Timer serviceTimer = new Timer();
serviceTimer.Interval = 60;
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
serviceTimer.Start();
bool isRunning = false;
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    lock (this)
    {
        if(isRunning)
            return;
        isRunning = true;
    }
    try
    {
    // do some heavy processing...
    }
    finally
    {
        isRunning = false;
    }
}

Solution 5

I recommend you don't let the timer tick at all while its processing.

Set the Timers AutoReset to false. And start it at the end. Here's a full answer you might be interested in Needed: A Windows Service That Executes Jobs from a Job Queue in a DB; Wanted: Example Code

Share:
19,302
gillyb
Author by

gillyb

Started out programming with php when I was 13 years old, mostly creating eCommerce applications for the web. Crossed over to .net programming during my service for the IDF. Was an engineer on the 'Core Performance' team at 'ShopYourWay.com' which is a site that belongs to 'Sears'. FED team leader at Logz.io Nov 2014 -> Nov 2017 (and first employee) Worked at Google. Currently at Palo Alto Networks. Check out My technical Blog! - www.DebuggerStepThrough.com or my personal blog - www.GillyBarr.com or my github account or my twitter account

Updated on June 17, 2022

Comments

  • gillyb
    gillyb almost 2 years

    I created a windows service, that is supposed to check a certain table in the db for new rows every 60 seconds. For every new row that was added, I need to do some heavy processing on the server that could sometimes take more than 60 seconds.

    I created a Timer object in my service, that ticks every 60 seconds and invokes the wanted method.
    Since I don't want this timer to tick while processing the new lines found, I wrapped the method in a lock { } block, so this won't be accessible by another thread.

    It looks something like this :

    Timer serviceTimer = new Timer();
    serviceTimer.Interval = 60;
    serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
    serviceTimer.Start();
    
    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        lock (this)
        {
            // do some heavy processing...
        }
    }
    

    Now, I'm wondering -
    If my timer ticks, and finds a lot of new rows on the db, and now the processing will take more than 60 seconds, the next tick won't do any processing till the previous one finished. This is the effect I want.

    But now, will the serviceTimer_Elapsed method go off immediatly once the first processing was finished, or will it wait for the timer to tick again.

    What I want to happen is - if the processing requires more than 60 seconds, than the timer will notice the thread is locked, and wait another 60 seconds to check again so I will never get stuck in a situation where there are a queue of threads waiting for the previous one to finish.

    How can i accomplish this result ?
    What is the best practice for doing this ?

    Thanks!