И System.Timers.Timer
, и System.Threading.Timer
срабатывают с интервалами, которые значительно отличаются от запрошенных.
Например:
new System.Timers.Timer(1000d / 20);
дает таймер, который срабатывает 16 раз в секунду, а не 20.
Чтобы убедиться в отсутствии побочных эффектов от слишком длинных обработчиков событий, я написал небольшую тестовую программу:
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);
}
Вывод выглядит так:
Запрошено: 5 Гц; актуально: 4,8 Гц
Запрошено: 10 Гц; актуально: 9,1 Гц
Запрошено: 15 Гц; актуально: 12,7 Гц
Запрошено: 20 Гц; актуально: 16 Гц
Запрошено: 30 Гц; актуально: 21,3 Гц
Запрошено: 50 Гц; актуально: 31,8 Гц
Запрошено: 75 Гц; актуально: 63,9 Гц
Запрошено: 100 Гц; актуально: 63,8 Гц
Запрошено: 200 Гц; актуально: 63,9 Гц
Запрошено: 500 Гц; актуально: 63,9 Гц
Фактическая частота отклоняется на 36% от запрошенной. (И, очевидно, не может превышать 64 Гц.) Учитывая, что Microsoft рекомендует этот таймер для «большей точности» над System.Windows.Forms.Timer
, это меня озадачивает.
Кстати, это не случайные отклонения. Они одинаковые значения каждый раз.
И аналогичная тестовая программа для другого класса таймера, System.Threading.Timer
, показывает точно такие же результаты.
В моей реальной программе мне нужно собирать измерения с частотой 50 выборок в секунду. Это еще не должно требовать системы реального времени. И очень неприятно получать 32 выборки в секунду вместо 50.
Есть идеи?
@ Крис:
Вы правы, интервалы кажутся целочисленными, кратными чему-то около 1/64 секунды. Кстати, добавление Thread.Sleep (...) в обработчик событий не имеет никакого значения. Это имеет смысл, учитывая, что System.Threading.Timer
использует пул потоков, поэтому каждое событие запускается в свободном потоке.