简体   繁体   中英

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.

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)

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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM