6
votes

I have a dll consumed by a service. Its basic job is to run every X minutes and perform some system checks. In my dll I have a top level class that declares a System.threading.timer and a Timercallback. The constructor for the class initialises the timerCallback with my thread function. In my "Onstart" handler I initialise the timer with the timercallback and set the next time to fire and interval time. In my case its every 10 minutes. Usually in these 10 minute checks there is nothing to do but the service is forced to do something at least once every day at a set time.

My problem: I am finding that during testing, the time the daily check is carried out every day is slowly drifitng away from the desired start time of 8.30. e.g. over about 20 odd days my time has drifted from 08.30 to 08.31.35. It drifts about 4 - 6 seconds every day.

My question: does anyone know why the time is drifting like this and how can I make it stick to its allotted time?

thanks

5
I'm not sure based on the question but it seems to be reading that you schedule a timer to do something in 10 minutes whose first action is to schedule another task in 10 minutes. Not scheduling a task every 10 minutes. Could you post the timer code?Sign
I only set the timer once, when the service first starts and I set it to fire every 10 minutes then. Its as though every 10 minute "tick" from then on gradually drifts later and later. Difficult to add code beecause I cant seem to put carriage returns in this texc box!Keith
@Keith - You are not suppose to have a full dicussion by comments. If you got code to post update your question.Security Hound

5 Answers

6
votes

The time "drifts" because the timer is simply not that precise. If you need to run your code as closely as possible to a certain interval, you can do something like this:

public void MyTimerCallback(object something) {
    var now = DateTime.UtcNow;
    var shouldProbablyHaveRun = new DateTime(
        now.Year, now.Month, now.Day,
        now.Hour, now.Minute - (now.Minute % 10), 0);
    var nextRun = shouldProbablyHaveRun.AddMinutes(10.0);

    // Do stuff here!

    var diff = nextRun - DateTime.UtcNow;
    timer.Change(diff, new TimeSpan(-1));
}

...assuming you are using a System.Threading.Timer instance. Modify the example if you are using any other type of timer (there are several!).

0
votes

Why not check every minute if the action needs to be performed?

ie:

if (DateTime.Now.Minute % 10) == 0 
0
votes

it takes a finite amount of time to do the operations you are doing in your timer method handler, so it makes sense that it's not going to happen every 10 minutes to the second, especially if you are scheduling the next wakeup after doing your checks and such. if you are already checking anyway for answering is it time to do this, you should make your timer fire more frequently to satisfy the resolution you need and trust your check of when it should execute something to make sure that it does. you probably need some sort of persistence to make sure it doesn't execute twice (if that is important) in case there is a shut down/restart and the state of knowing whether it has already run is not still in memory.

0
votes

Here is my take:

while ((DateTime.Now - lastRunTime).TotalSeconds < 600)
     CurrentThread.Sleep(1000);

or just register a windows timer and execute in response to the event/callback

public static void Main()
{
    System.Timers.Timer aTimer = new System.Timers.Timer();
    aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
    // Set the Interval to 600 seconds.
    aTimer.Interval=600000;
    aTimer.Enabled=true;

    Console.WriteLine("Press \'q\' to quit the sample.");
    while(Console.Read()!='q');
}

// Specify what you want to happen when the Elapsed event is raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("10 minutes passed!");
}
0
votes

Timers aren't exact, just approximate. Don't use the logic of "just add 10 minutes". Each time your timer fires, you need to check for time skew and adjust.

eg. If you say "wake me up in 10min", and it wakes you up in 10min 1sec, then the next timer needs to be 9min 59sec, not 10min.

Also, you want to assign your next timer at the end of your logic.

eg. say you want to start taskA every 10min and it takes 2 seconds to run. Your timer starts and 10 minutes later it wakes up to run taskA. It kicks off, finishes, now you add 10 minutes. But it took 2 seconds to run your task. So 10 minutes from the time your code ran will be skewed by 2 seconds.

What you need to do is predict the next time you need to run and find the difference between now and then and set the timer to that difference.