System.Threading.Timer causes a drift of a couple of seconds every day

I have a service that always works, it has a timer to perform a certain action every day at 2am.

TimeSpan runTime = new TimeSpan(2, 0, 0); // 2 AM TimeSpan timeToFirstRun = runTime - DateTime.Now.TimeOfDay; if (timeToFirstRun.TotalHours < 0) { timeToFirstRun += TimeSpan.FromDays(1.0); } _dailyNodalRunTimer = new Timer( RunNodalDailyBatch, null, timeToFirstRun, TimeSpan.FromDays(1.0)); //repeat event daily 

This initialization code is called once when the service is first started, in the last few days I went to the log when the Timer started:

 2011-05-21 02:00:01.580 2011-05-22 02:00:03.840 ... 2011-05-31 02:00:25.227 2011-06-01 02:00:27.423 2011-06-02 02:00:29.847 

As you can see, he drifts for 2 seconds every day, farther and farther from there when he was supposed to shoot (at 2 a.m.).

Am I using this incorrectly or is this timer not intended to pinpoint? I could recreate the timer every day or it has a fire at a short interval and repeatedly check if I want to perform an action, but this is like hacking.

EDIT

I tried using System.Timers.Timer and it seems to have the same problem. Resetting the interval is that you cannot schedule the start time before the first tick in System.Timers.Timer, as you can in System.Threading.Timer

 int secondsInterval = 5; double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval); var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0); timer.AutoReset = true; timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff")); if (timer.Interval != (secondsInterval * 1000.0)) timer.Interval = secondsInterval * 1000.0; }; timer.Start(); 

Make the following times, you can see how they drift a little:

 06:47:40.020 06:47:45.035 06:47:50.051 ... 06:49:40.215 06:49:45.223 06:49:50.232 

So, I think the best approach is to simply reschedule the timer in the tick handler? The following ticks at regular intervals for ~ 15 milliseconds

 double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval); var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0); timer.AutoReset = false; timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff")); timer.Interval = (secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval)) * 1000.0; }; timer.Start(); 06:51:45.009 06:51:50.001 ... 06:52:50.011 06:52:55.013 06:53:00.001 
+4
source share
5 answers

None of the timers in the .NET Framework will be accurate. There are too many variables in the game. If you want a more accurate timer, look at the multimedia timers . I have never used them for longer periods of time, but I suspect that they are still significantly more accurate than BCL timers.

But I see no reason that would forbid you to use the System.Threading.Timer class. Instead of specifying TimeSpan.FromDays(1) use Timeout.Infinite to prevent periodic signaling. Then you will need to restart the timer, but you can specify 23:59:58 or 1.00: 00: 05 for the dueTime parameter, depending on what you expect the next time to have a signal at 2: 00a.

By the way, System.Timers.Timer will not be better than System.Threading.Timer . The reason is that the first one actually uses the title scene anyway. System.Timers.Timer simply adds a few handy features, such as automatically resetting and marshaling Elapsed execution on an ISynchronizeInvoke hosted thread (usually a UI thread).

+3
source

Do not let the inaccuracy timer accumulate. Use RTC to calculate the number of ms before the timeout. Hibernate / setInterval until half this time. When the timer goes off / sleep, use RTC again to recount the interval to the left and set the interval / sleep again to half-life. Repeat this cycle until the remaining interval is less than 50 ms. Then the processor cycle on RTC until the required time is exceeded. Fire event.

Rgds, Martin

+4
source

I think you already understood this, but if you want something to work at a specific time of day (2AM), you would be better off with a dedicated thread that sleeps, wakes up periodically and looks to see if it will still have time to start. A sleep of about 100 milliseconds would be suitable and would burn almost no processor.

Another approach would be that after you have completed your daily work, you will calculate when the next fire will be based on tomorrow 2AM - DateTime.Current, etc. It may not be as accurate as you want (I'm not sure), but at least the drift will not be worse, worse or worse.

+3
source

If you need accurate synchronization, you will need the System.Timers.Timer class.

Also see this question: .NET, an event every minute (per minute). Is it better to use a timer?

+2
source

From msdn :

System.Threading.Timer is a simple, easy timer ... For the server-side timer function, you can use System.Timers.Timer , which raises events and has additional functions.

You can also move it to Windows Task Scheduler

+1
source

All Articles