Может ли класс .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()), но я понимаю, что что-то еще происходит здесь.
Любое понимание понимается.
Update:
Вот немного более подробно о моем коде: это довольно просто. Это приложение WPF, которое создает новый объект Stopwatch
при запуске, а затем отключает его через Start()
. Затем я создаю DispatcherTimer
, например:
displayTimer = new DispatcherTimer();
displayTimer.Tick += display_Tick;
displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN;
где время составляет 200 мкс. Мой код отладки просто выводит значение объекта Stopwatch
каждый раз, когда тики dispatchTimer
.
Отладочный журнал находится в dl.dropbox.com/u/7999907/hburg.txt. Первый бит данных в каждой строке - текущая дата/время через DateTime.UtcNow
. Время вправо - это значение через Stopwatch.Elapsed
. Вы можете увидеть, как прыгает секундомер, но DateTime не делает.
Update2:
Веселая статья поддержки Microsoft Значение счетчика производительности может неожиданно перескочить вперед.
Ответы
Ответ 1
Обновить (после просмотра вашего журнала)
Как вы уже упоминали, класс Stopwatch
использует QueryPerformanceCounter
под ним. В разделе Замечания MSDN говорит:
На многопроцессорном компьютере не имеет значения, какой процессор вызывается. Однако вы можете получать разные результаты на разных процессорах из-за ошибок в базовой системе ввода-вывода (BIOS) или слоя абстракции оборудования (HAL). Чтобы указать сродство процессора к потоку, используйте функцию SetThreadAffinityMask.
Поскольку вы используете диспетчер, QueryPerformanceCounter
может не выполняться на одном CPU каждый раз, когда вы запрашиваете прошедшее время.
Вы можете проверить, является ли проблема, упомянутая в MSDN, причиной вашей проблемы, указав сродство процессора к вашему процессу, например. вызывая ваш исполняемый файл с помощью команды start
. 10 секунд кажется большим отставанием между процессорами для меня, но документация очень расплывчата в отношении того, насколько велика разница. Следующая команда привяжет ваше приложение к первому процессору:
> start.exe /AFFINITY 1 program.exe
Если это должно решить проблему, вы можете захотеть взглянуть на предлагаемое решение, то есть вызвать функцию SetThreadAffinityMask
перед запросом объекта Stopwatch
.
<ы > Ваш комментарий сказал, что вы используете WPF DispatcherTimer
. В документации этого класса указано:
Таймеры не гарантируют выполнение точно, когда происходит интервал времени, но они гарантированно не выполняются до того, как произойдет интервал времени. Это связано с тем, что операции DispatcherTimer помещаются в очередь диспетчера, как и другие операции. При выполнении операции DispatcherTimer зависит от других заданий в очереди и их приоритетов.
Это означает, что событие таймера может задерживаться, особенно если диспетчер занят другими задачами. Вы размещали другие вещи в очереди диспетчера, что предотвратит запуск события раньше?
С >
Ответ 2
Точный выбор времени для аппаратного обеспечения ПК не является простым, периодом. Вот некоторые ресурсы, демонстрирующие некоторый опыт синхронизации в Windows, который будет основной реализацией любого счетчика в среде Windows, включая .NET:
Это объясняет, почему иногда вы получаете разрешение 10 ms, в зависимости от того, какой системный вызов вы используете, и проливает немного больше света на то, почему QueryPerformanceCounter "глючит". Один из пунктов состоит в том, что режимы энергосбережения/переменные скорости процессора могут помешать этим таймингам.
Связанная с этим концепция - "блокировка времени" в физическом моделировании в реальном времени. Если вы Google для этого, вы можете получить некоторые идеи о том, как работать с проблемами, которые у вас есть. Основная концепция заключается в том, что у вас есть фиксированные временные шаги, и вы делаете своего рода реализацию производителя/потребителя для ваших функций синхронизации/обновления. Я не знаю, относится ли это к вашему конкретному домену, но считается лучшим в видеоиграх.
Ответ 3
Возможно, я не понимаю ваш вопрос, но нужно ли просто измерять интервалы времени или вам нужно выполнять код на этих интервалах? Если вам нужно выполнить код, то проверьте класс System.Timers.Timer, поскольку он потокобезопасен и должен отлично работать для нескольких процессоров.