Оба 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
, это меня озадачивает.
Btw, это не случайные отклонения. Они одинаковые значения каждый раз.
И аналогичная тестовая программа для другого класса таймера System.Threading.Timer
показывает те же самые результаты.
В моей реальной программе мне нужно собирать измерения ровно 50 выборок в секунду. Это еще не требует системы реального времени. И очень сложно получить 32 выборки в секунду вместо 50.
Любые идеи?
: @Крис
Вы правы, все интервалы кажутся целыми кратными примерно 1/64-й секунде. Btw, добавив Thread.Sleep(...) в обработчик события, не имеет никакого значения. Это имеет смысл, учитывая, что System.Threading.Timer
использует пул потоков, поэтому каждое событие запускается в свободном потоке.