Clock drift. Very typical for trivial timers. The reason for this is that they are typically implemented using a sleep function. A sleep function is always guaranteed to sleep for at least the time specified but not guaranteed to not sleep for more time than that, in practice it's always accumulating a drift.
There are ways to write timer that compensate for the drift and on average hit the target.
One of my favorite timers are fixed step ones, that have a discreet tick. It's very simple and looks like this:
var t = DateTime.Now + TimeSpan.FromSeconds(1);
for (;;)
{
if (DateTime.Now >= t)
{
t += TimeSpan.FromSeconds(1); // Tick!
}
}
That's a crude but effective timer, this following one is an example of a clock I built for a WPF timer where the built in timer was suffering from drifting. This timer is a lot more complex and it does not hog your CPU. But it clearily illustrates typical issues that timers have.
The OnTimerTick here is using a built in timer that suffers from drift but it's adjusting the interval to compensate for the drift.
/// <summary>
/// Occurs when the timer interval has elapsed.
/// </summary>
public event EventHandler Tick;
DispatcherTimer timer;
public bool IsRunning { get { return timer.IsEnabled; } }
long step, nextTick, n;
public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }
public FixedStepDispatcherTimer(TimeSpan interval)
{
if (interval < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("interval");
}
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(OnTimerTick);
this.step = interval.Ticks;
}
TimeSpan GetTimerInterval()
{
var interval = nextTick - DateTime.Now.Ticks;
if (interval > 0)
{
return new TimeSpan(interval);
}
return TimeSpan.Zero; // yield
}
void OnTimerTick(object sender, EventArgs e)
{
if (DateTime.Now.Ticks >= nextTick)
{
n++;
if (Tick != null)
{
Tick(this, EventArgs.Empty);
}
nextTick += step;
}
var interval = GetTimerInterval();
Trace.WriteLine(interval);
timer.Interval = interval;
}
public void Reset()
{
n = 0;
nextTick = DateTime.Now.Ticks;
}
public void Start()
{
var now = DateTime.Now.Ticks;
nextTick = now + (step - (nextTick % step));
timer.Interval = GetTimerInterval();
timer.Start();
}
public void Stop()
{
timer.Stop();
nextTick = DateTime.Now.Ticks % step;
}