DispatchTimer does not execute on time

2020-05-09 06:20发布

问题:

I'm coding a clock app using DispatchTimer in c#, but for some reasons my clock seems to skip 1 second every now and then.

eg. 52s -> 54s skipping 53s.

Seems to me that the timer does not execute exactly every second.

DispatcherTimer timer = new DispatcherTimer();

timer.Tick += DispatcherTimerEventHandler;

timer.Interval = new TimeSpan(0, 0, 0, 0, 1000);

or

timer.Interval = new TimeSpan(0, 0, 0, 1);

Both also don't work.

回答1:

Basically your approach is wrong. Don't add "one second" to a TimeSpan (or any kind of counter) based on any kind of timer, because in Windows, timers aren't guaranteed to fire at the exact interval. Using that approach will result in "drift" over longer periods of time.

Instead, store a DateTime "start" value and subtract that from the current DateTime to get a TimeSpan representing how much time has transpired since "start". With that approach it won't matter how often you update the clock. You could update 10 times a second, or once a minute, and the TimeSpan returned will still be correct.

An alternative is to use the Stopwatch class. It encapsulates the above process for you by returning a TimeSpan from its Elapsed() property.

With either of the two approaches above, it wouldn't matter how often the Timer fires as it will keep accurate time independent of the Timer frequency/timing.



回答2:

From the documentation of the DispatchTimer (emphasis mine):

Timers are not guaranteed to execute exactly when the time interval occurs, but they are guaranteed to not execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.

Usually I would recommend to use some kind of scheduling/cron framework like Quartz.NET, but this seems like a huge overhead for your usecase.
For a "clock app", although it's quite difficult to know what exactly you want to do, I would go for my own solution - meaning some kind of new thread with a while-loop or a BackgroundWorker.
Even a timer may help you, for example like in this answer.



标签: c# timer