Может ли класс .NET Секундомер быть таким ужасным? - PullRequest
17 голосов
/ 03 августа 2010

Я делаю приложение, которое требует довольно сжатых сроков, и класс Секундомер является идеальным решением. Тем не менее, я иногда замечал, что при работе на маленьком ПК значения секундомера были слишком большими. Я добавил несколько отладочных распечаток, которые отслеживают значение секундомера каждые 200 мс или около того:

0: 00: 197
0: 00: 502
0: 00: 702
...
0: 03: 356
0: 12: 93

0: 13: 21
0: 13: 421
...

Как он мог прыгнуть с ~ 3 секунд до ~ 13 секунд? Теперь я вижу, что базовая функция QueryPerformanceCounter () содержит ошибки ( Остерегайтесь QueryPerformanceCounter () ), но я понимаю, что здесь происходит что-то еще.

Любое понимание приветствуется.

Обновление:

Вот немного больше деталей в моем коде: это довольно просто. Это приложение WPF, которое при запуске создает новый объект Stopwatch, а затем запускает его с помощью Start(). Затем я создаю DispatcherTimer, вот так:

displayTimer = new DispatcherTimer(); 
displayTimer.Tick += display_Tick; 
displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN; 

где временной интервал составляет 200 мс. Мой код отладки просто выводит значение объекта Stopwatch каждый раз, когда dispatchTimer тикает.

Update2:

Интересная статья поддержки Microsoft: Значение счетчика производительности может неожиданно подскочить вперед .

Ответы [ 3 ]

17 голосов
/ 04 августа 2010

Обновление (после просмотра вашего журнала)

Как вы уже упоминали, класс Stopwatch использует функцию QueryPerformanceCounter внизу.В разделе Замечания MSDN гласит:

На многопроцессорном компьютере не должно иметь значения, какой процессор вызывается.Однако вы можете получить разные результаты на разных процессорах из-за ошибок в базовой системе ввода / вывода (BIOS) или на уровне абстрагирования оборудования (HAL).Чтобы указать привязку процессора к потоку, используйте функцию SetThreadAffinityMask.

Поскольку вы используете Диспетчер, QueryPerformanceCounter может не выполняться на одном и том же ЦП каждый раз, когда вы запрашиваете истекшее время.

Вы можете проверить, упоминалась ли проблемав MSDN причина вашей проблемы, указав привязку процессора к вашему процессу, например, вызвав ваш исполняемый файл с помощью команды start.Мне кажется, что 10 секунд - большая задержка между процессорами, но документация о том, насколько велика разница, очень расплывчата.Следующая команда свяжет ваше приложение с первым процессором:

> start.exe /AFFINITY 1 program.exe

Если это решит проблему, вы можете захотеть взглянуть на предлагаемый обходной путь, то есть вызвать функцию SetThreadAffinityMask перед запросом Stopwatch объект.

В вашем комментарии сказано, что вы используете WPF DispatcherTimer. документация этого класса гласит:

Таймеры не гарантированно работают точно, когда временной интервал наступает , но онигарантированно не будет выполнено до наступления временного интервала.Это связано с тем, что операции DispatcherTimer помещаются в очередь Dispatcher, как и другие операции.Время выполнения операции DispatcherTimer зависит от других заданий в очереди и их приоритетов.

Это означает, что событие таймера может поступить с задержкой, особенно если диспетчер занят другими задачами.Вы поместили другие вещи в очередь диспетчера, которые будут препятствовать тому, чтобы событие вызвало ранее?

15 голосов
/ 03 августа 2010

Точное время на ПК не так просто, точка. Вот некоторые ресурсы, демонстрирующие опыт работы с синхронизацией в Windows, которая будет лежать в основе реализации любого счетчика в среде Windows, включая .NET:

Это объясняет, почему иногда вы получаете разрешение 10 мс, в зависимости от того, какой системный вызов вы используете, и проливает немного больше света на то, почему QueryPerformanceCounter «глючит». Один из замечаний заключается в том, что режимы энергосбережения / переменные скорости процессора могут мешать этим временным параметрам.

С этим связано понятие «блокирование временного шага» в физическом моделировании в реальном времени. Если вы воспользуетесь Google для этого, вы можете получить некоторые идеи о том, как обойти возникающие проблемы. Основная концепция заключается в том, что у вас есть фиксированные временные шаги, и вы делаете своего рода реализацию производителя / потребителя для ваших функций синхронизации / обновления. Я не знаю, относится ли это к вашему конкретному домену, но это считается наилучшей практикой в ​​видеоиграх.

2 голосов
/ 04 августа 2010

Возможно, я неправильно понимаю ваш вопрос, но нужно ли вам просто измерять временные интервалы или вам нужно выполнять код с этими интервалами?Если вам нужно выполнить код, то проверьте класс System.Timers.Timer, так как он является поточно-ориентированным и должен нормально работать для многопроцессорных систем.

...