Может ли класс .NET Stopwatch быть НАСТОЯЩИМ ужасным?

Я делаю приложение, которое требует довольно сжатого времени, и класс Stopwatch - идеальное решение. Однако иногда я замечал, что при работе на небольшом панельном ПК значения секундомера были неверными. Я добавил несколько отладочных распечаток, которые отслеживают значение секундомера каждые 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.

Обновление2:

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


person BabaBooey    schedule 03.08.2010    source источник
comment
Вы не сказали нам ничего о том, что происходит между этими измерениями. Возможно, ваше приложение зависло? Что произойдет, если вы одновременно распечатаете DateTime.UtcNow?   -  person Jon Skeet    schedule 03.08.2010
comment
Как вы гарантируете, что код будет вызываться ровно каждые 200 мс?   -  person Justin Niessner    schedule 03.08.2010
comment
глючит он может быть, но глючит не так :)   -  person µBio    schedule 03.08.2010
comment
согласованный. 10 секунд недопустимо.   -  person Chase Florell    schedule 04.08.2010
comment
Может быть, вы предполагаете, что таймеры перестают продвигаться, когда вы достигли точки останова? Это не так, хотя было бы неплохо. Я часто вижу, что код, зависящий от времени, ведет себя неправильно, потому что я провел минуту в точке останова, и он предполагает меньшие интервалы.   -  person Craig Gidney    schedule 04.08.2010
comment
Вот немного подробностей о моем коде ... это довольно просто. Это приложение WPF, которое создает новый объект Stopwatch при запуске, а затем запускает его через .Start (). Затем я создаю DispatcherTimer следующим образом: displayTimer = new DispatcherTimer (); displayTimer.Tick + = display_Tick; displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN; где временной интервал составляет 200 мс. Мой код отладки просто выводит значение объекта Stopwatch каждый раз, когда срабатывает dispatchTimer. У меня почти закончились символы, но я хотел бы отобразить дополнительную информацию об отладке.   -  person BabaBooey    schedule 04.08.2010
comment
Вот ссылка на журнал отладки: dl.dropbox.com/u/7999907/hburg .txt. Первый бит данных в каждой строке - это текущая дата / время через DateTime.UtcNow. Время до конца вправо - это значение через секундомер. Вы можете видеть, как прыгает секундомер, а DateTime - нет.   -  person BabaBooey    schedule 04.08.2010
comment
Вы уверены, что не сталкиваетесь с проблемой времени потоковой передачи, когда Машина просто заменяла выполняющийся процесс? У маленького панельного ПК могло быть настолько мало мощности, что это могло произойти. (это маловероятно)   -  person CodingBarfield    schedule 05.06.2013
comment
Я должен отметить, что эта проблема была в Windows XP, но устранена в WIn Vista, и вместо этого они используют менее точный счетчик в контроллере ввода-вывода материнской платы. Он имеет меньшую точность, но не привязан к ядру.   -  person Bogdan Mart    schedule 17.11.2016


Ответы (3)


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

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

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

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

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

> start.exe /AFFINITY 1 program.exe

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

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

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

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

person Dirk Vollmar    schedule 03.08.2010
comment
Понятно, но dispatcherTimer просто следит за секундомером. Тик dispatcherTimer на самом деле не должен быть точным для меня ... Я просто не могу поглотить 10-секундный скачок в моем секундомере :) - person BabaBooey; 04.08.2010
comment
@ Тим: Хорошо, я еще не видел ваш файл журнала, когда ответил. Можете ли вы создать небольшой образец приложения для воспроизведения артефакта? Кстати, вы можете редактировать свой вопрос и включать дополнительную информацию. - person Dirk Vollmar; 04.08.2010
comment
Интересный. К сожалению, я работаю на небольшом панельном ПК (с Windows Embedded Standard) с одним процессором. Я не очень знаком с start.exe, но для меня нет доступного переключателя AFFINITY. - person BabaBooey; 04.08.2010
comment
@ Тим: Хорошо, это действительно важный факт, что вы наблюдаете это на встроенной системе, а не на обычном настольном ПК. Поскольку часы в конце концов основаны на оборудовании, это делает аппаратную ошибку наиболее вероятной. - person Dirk Vollmar; 04.08.2010
comment
Вот чего я боялся. Полагаю, я могу запустить свой собственный секундомер, который не использует QueryPerformanceCounter. Особенно неприятно, когда вызов библиотеки работает не так, как заявлено. Я имею в виду, что я могу справиться со своими вещами, которые не работают, но не с чужими :) - person BabaBooey; 04.08.2010

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

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

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

person Merlyn Morgan-Graham    schedule 03.08.2010
comment
Разрешение 10 мс мне подходит ... Я вижу, как класс Stopwatch делает скачок на 10 СЕКУНД вперед. - person BabaBooey; 04.08.2010
comment
Согласен ... 10 секунд - это безумие. - person Chase Florell; 04.08.2010
comment
Может, может, и правда прошло 10 секунд? Что на самом деле делала ваша программа? - person dlras2; 04.08.2010
comment
@ Тим: Это самое близкое к разгадке вашей проблемы большинство людей. Вам нужно будет показать минимальный код, который воспроизводит проблему, и рассказать о вашей среде, чтобы мы перестали гадать :) - person Merlyn Morgan-Graham; 04.08.2010
comment
Десять секунд могут быть довольно сумасшедшими, но это вполне возможно, если сработает сборщик мусора. - person Matthew Whited; 04.08.2010

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

person jle    schedule 03.08.2010
comment
И то, и другое :) Мне нужно время от времени запускать метод, и мне нужно измерять временные интервалы. - person BabaBooey; 04.08.2010