I think the other answers are failing to address why there's 14ms slew through each iteration of the OP's code; it's not because of an imprecise system clock (and DateTime.Now
is not inaccurate, unless you've turned off NTP services or have the wrong time zone set or something silly! It's only imprecise).
Accurate timer
Even with an imprecise system clock (making use of DateTime.Now
, or having a solar cell hooked up to an ADC to tell how high the sun is in the sky, or dividing the time between peak tides, or ...), code following this pattern will have an average of zero slew (it will be perfectly accurate with exactly one second between ticks on average):
var interval = new TimeSpan(0, 0, 1);
var nextTick = DateTime.Now + interval;
while (true)
{
while ( DateTime.Now < nextTick )
{
Thread.Sleep( nextTick - DateTime.Now );
}
nextTick += interval; // Notice we're adding onto when the last tick
// was supposed to be, not when it is now.
// Insert tick() code here
}
(If you're copying-and-pasting this, watch out for cases where your tick code takes longer than interval
to execute. I'll leave it as an exercise for the reader to find the easy ways to make this skip as many beats as it takes for nextTick
to land in the future)
Inaccurate timer
I'm guessing that Microsoft's implementation of System.Threading.Timer
follows this kind of pattern instead. This pattern will always have slew even with a perfectly precise and perfectly accurate system timer (because it takes time to execute even just the add operation):
var interval = new TimeSpan(0, 0, 1);
var nextTick = DateTime.Now + interval;
while (true)
{
while ( DateTime.Now < nextTick )
{
Thread.Sleep( nextTick - DateTime.Now );
}
nextTick = DateTime.Now + interval; // Notice we're adding onto .Now instead of when
// the last tick was supposed to be. This is
// where slew comes from.
// Insert tick() code here
}
So for folks who might be interested in rolling your own timer, don't follow this second pattern.
Precise time measurement
As other posters have said, the Stopwatch
class gives great precision for time measurement, but doesn't help at all with accuracy if the wrong pattern is followed. But, as @Shahar said it's not like you're ever going to get a perfectly-precise timer to begin with, so you need to rethink things if perfect precision is what you're after.
Disclaimers
Note that Microsoft doesn't talk much about the internals of the System.Threading.Timer
class so I'm educatedly speculating about it, but if it quacks like a duck then it's probably a duck. Also, I realize this is several years old, but it's still a relevant (and I think unanswered) question.
Edit: Changed link to @Shahar's answer
Edit: Microsoft has source code for a lot of stuff online, including System.Threading.Timer
, for anyone who is interested in seeing how Microsoft implemented that slew-y timer.