Вычисление количества секунд между двумя моментами времени в Cocoa, даже если системные часы изменились на полпути
-
06-07-2019 - |
Вопрос
Я пишу программу для конечного пользователя Cocoa OS X (Leopard 10.5+), которая использует временные метки для вычисления статистики того, как долго что-то отображается на экране.Время периодически вычисляется во время выполнения программы с использованием повторяющегося NSTimer. [NSDate date]
используется для захвата временных меток, Начать и Финиш.Вычисление разницы между двумя датами в секундах является тривиальным.
Проблема возникает, если конечный пользователь или ntp изменяет системные часы. [NSDate date]
зависит от системных часов, поэтому, если они изменены, Финиш переменная будет искажена относительно Начать, значительно искажая расчет времени.Мой вопрос:
1.Как я могу точно рассчитать время между Начать и Финиш, в секундах, даже когда системные часы меняются на полпути?
Я думаю, что мне нужна неизменяющаяся точка отсчета во времени, чтобы я мог подсчитать, сколько секунд прошло с тех пор.Например, время безотказной работы системы.10.6 имеет - (NSTimeInterval)systemUptime
, часть NSProcessInfo
, что обеспечивает время безотказной работы системы.Однако это не сработает, так как мое приложение должно работать в версии 10.5.
Я пытался создать счетчик времени с помощью NSTimer, но это неточно.NSTimer имеет несколько различных режимов запуска и может запускаться только по одному одновременно.NSTimer (по умолчанию) помещается в По умолчанию режим запуска.Если пользователь начинает манипулировать пользовательским интерфейсом в течение достаточно длительного времени, это приведет к Режим отслеживания nseventrunloopmode и пропустите через По умолчанию режим запуска, который может привести к пропуску срабатываний NSTimer, что делает его неточным способом подсчета секунд.
Я также думал о создании отдельного потока (NSRunLoop) для запуска второго счетчика NSTimer, держа его подальше от взаимодействий с пользовательским интерфейсом.Но я очень новичок в многопоточности и хотел бы держаться от этого подальше, если это возможно.Кроме того, я не уверен, будет ли это работать точно в случае, если процессор будет привязан к другому приложению (Photoshop, рендеринг большого изображения и т.д.), В результате чего мой NSRunLoop будет приостановлен на достаточно долгое время, чтобы испортить его NSTimer.
Я ценю любую помощь.:)
Решение 3
Я нашел способ сделать это, используя Время безотказной работы () Функция C, предусмотренная в <CoreServices/CoreServices.h>
.Это возвращает абсолютное время (зависящее от процессора), которое можно легко преобразовать в длительность (миллисекунды или наносекунды).Подробности здесь: http://www.meandmark.com/timingpart1.html (время безотказной работы смотрите в разделе часть 3)
Я не мог получить mach_absolute_time()
для правильной работы, вероятно, из-за моего недостатка знаний об этом и невозможности найти много документации в Интернете по этому поводу.Похоже, что он захватывает то же время, что и UpTime()
, но преобразование его в дубль ошарашило меня.
[[NSApp currentEvent] timestamp]
сработало, но только в том случае, если приложение получало NSEvents.Если бы приложение вышло на передний план, оно не получало бы события, и [[NSApp currentEvent] timestamp]
просто продолжал бы возвращать одну и ту же старую временную метку снова и снова в методе запуска NSTimer, пока конечный пользователь не решил бы снова взаимодействовать с приложением.
Спасибо за вашу помощь Марку и Майку!Вы оба определенно направили меня в правильном направлении, ведущем к ответу.:)
Другие советы
В зависимости от того, что движет этим кодом, у вас есть 2 варианта:
- Для достижения абсолютной точности используйте
mach_absolute_time()
.Это даст интервал времени точно между точками, в которых вы вызвали функцию. - Но в приложении с графическим интерфейсом это часто на самом деле нежелательно.Вместо этого вы хотите, чтобы разница во времени между Мероприятия на этом началась и закончилась ваша продолжительность.Если да, сравните
[[NSApp currentEvent] timestamp]
Ладно, это рискованно, но вы могли бы попробовать реализовать что-то вроде NSSystemClockDidChangeNotification
доступно в Snow Leopard.
Так что потерпите меня здесь, потому что это странная идея и определенно не является дертерминистской.Но что, если бы у вас был сторожевой поток, работающий на протяжении всей вашей программы?Этот поток будет каждые n секунд считывать системное время и сохранять его.Ради аргументации, давайте просто подождем 5 секунд.Таким образом, каждые 5 секунд он сравнивает предыдущие показания с текущим системным временем.Если есть "достаточно большая" разница ("достаточно большая" определенно должно быть больше 5, но не слишком много больше, чтобы учесть недетерминированность планирования процесса и приоритизации потоков), опубликуйте уведомление о значительном изменении времени.Вам нужно было бы поиграть с размытием значения, которое составляет "достаточно большое" (или достаточно маленькое, если часы были сброшены на более раннее время) для ваших потребностей в точности.
Я знаю, что это своего рода халтура, но, за исключением любого другого решения, что вы думаете?Может ли это или что-то в этом роде решить вашу проблему?
Редактировать
Итак, вы изменили свой первоначальный вопрос, указав, что предпочли бы не использовать сторожевой поток, потому что вы новичок в многопоточности.Я понимаю страх делать что-то немного более продвинутое, чем вам удобно, но в конечном итоге это может оказаться единственным решением.В этом случае вам, возможно, придется немного почитать.=)
И да, я знаю, что что-то вроде Photoshop, связывающее дерьмо с процессором, является проблемой.Другим (еще более сложным) решением было бы вместо сторожевого пса Нитки, иметь отдельного сторожевого пса процесс это имеет высший приоритет, поэтому оно немного более устойчиво к привязке процессора.Но опять же, это становится действительно сложным.
Окончательное редактирование
Я собираюсь оставить все мои другие идеи выше для полноты картины, но, похоже, использование времени безотказной работы системы также будет правильным способом справиться с этим.С тех пор как [[NSProcessInfo processInfo] systemUptime]
работает только в версии 10.6 +, вы можете просто вызвать mach_absolute_time()
.Чтобы получить доступ к этой функции, просто #include <mach/mach_time.h>
.Это должно быть то же значение, что и возвращаемое NSProcessInfo
.