jjss
jjss

Reputation: 93

How to call a method every minute but take into account the time it takes for that method to process might take more than one?

I'm working on windows service and I want to call a method from OnStart every minute. I originally had a forever while loop but then the service wouldn't install.

while (true)
{
    Stopwatch stopWatch = new Stopwatch();
    int totalTime = 0;
    stopWatch.Start();

    MethodToCall();

    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;
    totalTime = ts.Seconds * 1000 + ts.Milliseconds;

    if (totalTime < 60000)
    {
        Thread.Sleep(60000 - totalTime);
        //ManualResetEvent.WaitOne(10000);
    }
    else
    {
        Thread.Sleep(30000);
    }
}

So, how can I make my method call every minute BUT when the method exceeds one minute it will wait N number of minutes(let's say 30 seconds) and then start over by calling the method.

Upvotes: 0

Views: 1769

Answers (2)

Jonathan Tyson
Jonathan Tyson

Reputation: 463

Something like this should work. With the AutoReset flag set to false, the timer will only fire once, after the specified interval time. In the finally block, we make sure to restart the timer countdown, waiting for the interval to elapse again.

var interval = TimeSpan.FromMinutes( 1 );
var timer = new System.Timers.Timer( interval.TotalMilliseconds ) { AutoReset = false };
timer.Elapsed += ( sender, eventArgs ) =>
{
    var start = DateTime.Now;
    try
    {
        // do work
    }
    finally
    {
        var elapsed = DateTime.Now - start;
        if ( elapsed < interval )
            timer.Interval = (interval - elapsed).TotalMilliseconds;
        else
            timer.Interval = TimeSpan.FromSeconds( 30 ).TotalMilliseconds;
        timer.Start();
    }
};
timer.Start();

Source for Timer.Elapsed (note the bit about setting Interval resetting the countdown)

Upvotes: 1

Ray Fischer
Ray Fischer

Reputation: 996

There are two solutions depending on what you want. Do you want to do work once a minute on the minute and always wait for the next minute? Or do you want to run no more than once a minute but it's okay to "catch up" if you fall behind?

In other words, if processing takes 80 seconds then does the next work start immediately or wait until T=120?

The first is easier, but note that I haven't tested this and it's just a guideline:

AutoResetEvent waitHandle = new AutoResetEvent(false);
System.Timer(() => waitHandle.Set(), null, TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(-1));

while (true)
{
  // Do stuff
  waitHandle.WaitOne();
}

The second is just a bit harder.

ManualResetEvent waitHandle = new ManualResetEvent (false);
System.Timer(() => waitHandle.Set(), null, TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(-1));

while (true)
{
  // Do stuff
  waitHandle.Reset();
  waitHandle.WaitOne();
}

Upvotes: 0

Related Questions