Вычисление количества секунд между двумя моментами времени в Cocoa, даже если системные часы изменились на полпути

StackOverflow https://stackoverflow.com/questions/1627764

Вопрос

Я пишу программу для конечного пользователя 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top