Класс Секундомера.NET может быть ЭТИМ ужасным?

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

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

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

Update2:

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

17
задан DeanOC 10 July 2018 в 01:33
поделиться

3 ответа

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

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

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

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

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

> start.exe /AFFINITY 1 program.exe

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

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

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

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

17
ответ дан 30 November 2019 в 12:06
поделиться

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

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

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

15
ответ дан 30 November 2019 в 12:06
поделиться

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

2
ответ дан 30 November 2019 в 12:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: