Both System.Timers.Timer
and System.Threading.Timer
fire at intervals that are considerable different from the requested ones.
For example:
new System.Timers.Timer(1000d / 20);
yields a timer that fires 16 times per second, not 20.
To be sure that there are no side-effects from too long event handlers, I wrote this little test program:
int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };
// Test System.Timers.Timer
foreach (int frequency in frequencies)
{
int count = 0;
// Initialize timer
System.Timers.Timer timer = new System.Timers.Timer(1000d / frequency);
timer.Elapsed += delegate { Interlocked.Increment(ref count); };
// Count for 10 seconds
DateTime start = DateTime.Now;
timer.Enabled = true;
while (DateTime.Now < start + TimeSpan.FromSeconds(10))
Thread.Sleep(10);
timer.Enabled = false;
// Calculate actual frequency
Console.WriteLine(
"Requested frequency: {0}\nActual frequency: {1}\n",
frequency, count / 10d);
}
The output looks like this:
Requested: 5 Hz; actual: 4,8 Hz
Requested: 10 Hz; actual: 9,1 Hz
Requested: 15 Hz; actual: 12,7 Hz
Requested: 20 Hz; actual: 16 Hz
Requested: 30 Hz; actual: 21,3 Hz
Requested: 50 Hz; actual: 31,8 Hz
Requested: 75 Hz; actual: 63,9 Hz
Requested: 100 Hz; actual: 63,8 Hz
Requested: 200 Hz; actual: 63,9 Hz
Requested: 500 Hz; actual: 63,9 Hz
The actual frequency deviates by up to 36% from the requested one. (And evidently cannot exceed 64 Hz.) Given that Microsoft recommends this timer for its "greater accuracy" over System.Windows.Forms.Timer
, this puzzles me.
Btw, these are not random deviations. They are the same values every time.
And a similar test program for the other timer class, System.Threading.Timer
, shows the exact same results.
In my actual program, I need to collect measurements at precisely 50 samples per second. This should not yet require a real-time system. And it is very frustrating to get 32 samples per second instead of 50.
Any ideas?
@Chris:
You are right, the intervals all seem to be integer multiples of something around 1/64th second. Btw, adding a Thread.Sleep(...) in the event handler doesn't make any difference. This makes sense given that System.Threading.Timer
uses the thread pool, so each event is fired on a free thread.