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

Я пишу программу для конечных пользователей Cocoa OS X (Leopard 10.5+), которая использует временные метки для расчета статистики того, как долго что-то отображается на экране. Время рассчитывается периодически, пока программа работает с использованием повторяющегося NSTimer. [NSDate date] используется для захвата меток времени, начала и окончания. Вычисление разницы между двумя датами в секундах тривиально.

Проблема возникает, если конечный пользователь или NTP изменяет системные часы. [NSDate date] полагается на системные часы, поэтому, если они будут изменены, переменная Finish будет смещена относительно начала, что значительно испортит расчет времени. Мой вопрос:

1. Как я могу точно рассчитать время между стартом и финишем, в секундах, даже когда системные часы меняются на полпути?

Я думаю, что мне нужен неизменяемый ориентир во времени, чтобы я мог рассчитать, сколько секунд прошло с тех пор. Например, время работы системы. 10,6 имеет - (NSTimeInterval)systemUptime, часть NSProcessInfo, что обеспечивает работоспособность системы. Тем не менее, это не будет работать, так как мое приложение должно работать в 10.5.

Я пытался создать счетчик времени с помощью NSTimer, но это не точно. NSTimer имеет несколько различных режимов запуска и может запускаться только по одному за раз. NSTimer (по умолчанию) переводится в режим запуска по умолчанию. Если пользователь начинает манипулировать пользовательским интерфейсом в течение достаточно долгого времени, он войдет в NSEventTrackingRunLoopMode и пропустит режим запуска по умолчанию, что может привести к пропуску срабатываний NSTimer, что делает его неточным способом подсчета секунд.

Я также подумал о создании отдельного потока (NSRunLoop) для запуска счетчика секунд NSTimer, не допуская его взаимодействия с пользовательским интерфейсом. Но я очень плохо знаком с многопоточностью и хотел бы держаться подальше от этого, если это возможно. Кроме того, я не уверен, что это будет работать точно в случае, если ЦП привязан другим приложением (Photoshop отображает большое изображение и т. Д.), В результате чего мой NSRunLoop будет удерживаться достаточно долго, чтобы испортить его. NSTimer.

Я ценю любую помощь.:)

3 ответа

Решение

Я нашел способ сделать это с помощью функции UpTime () C, представленной в <CoreServices/CoreServices.h>, Это возвращает абсолютное время (зависит от процессора), которое можно легко преобразовать в длительность (миллисекунды или наносекунды). Подробности здесь: http://www.meandmark.com/timingpart1.html (смотрите UpTime в части 3)

Я не мог получить mach_absolute_time() работать должным образом, вероятно, из-за моего отсутствия знаний об этом, и не в состоянии найти много документации в Интернете об этом. Похоже, захватить в то же время, что и UpTime(), но превращение его в двойное оставило меня ошеломленным.

[[NSApp currentEvent] timestamp] работал, но только если приложение получало NSEvents. Если приложение вышло на передний план, оно не получало бы события, и [[NSApp currentEvent] timestamp] будет просто продолжать возвращать одну и ту же старую временную метку снова и снова в методе запуска NSTimer, пока конечный пользователь не решит снова взаимодействовать с приложением.

Спасибо за вашу помощь, Марк и Майк! Вы оба определенно отправили меня в правильном направлении, что привело к ответу.:)

В зависимости от того, что движет этим кодом, у вас есть 2 варианта:

  • Для абсолютной точности используйте mach_absolute_time(), Это даст интервал времени точно между точками, в которых вы вызвали функцию.
  • Но в приложении с графическим интерфейсом это часто нежелательно. Вместо этого вы хотите разницу во времени между событиями, которые начались и закончили вашу продолжительность. Если так, сравните [[NSApp currentEvent] timestamp]

Итак, это длинный выстрел, но вы можете попробовать реализовать что-то вроде NSSystemClockDidChangeNotification доступно в снежном барсе.

Так что терпите меня здесь, потому что это странная идея и определенно недерминистская. Но что, если у вас был сторожевой поток, работающий на протяжении всей вашей программы? Этот поток каждые n секунд считывает системное время и сохраняет его. Ради аргумента, давайте просто сделаем это 5 секунд. Таким образом, каждые 5 секунд он сравнивает предыдущее значение с текущим системным временем. Если существует "достаточно большая" разница ("достаточно большая" должна определенно быть больше 5, но не слишком большой, чтобы учесть недетерминированность планирования процессов и приоритезации потоков), опубликуйте уведомление о том, что значительное изменение времени. Вам нужно поиграть с размытым значением, которое составляет "достаточно большое" (или достаточно маленькое, если часы были сброшены на более раннее время) для вашей точности.

Я знаю, что это отчасти хакерски, но, за исключением любого другого решения, как вы думаете? Может ли это или что-то в этом роде решить вашу проблему?

редактировать

Итак, вы изменили свой первоначальный вопрос, сказав, что вы не хотите использовать сторожевой поток, потому что вы новичок в многопоточности. Я понимаю страх делать что-то более продвинутое, чем вам удобно, но это может оказаться единственным решением. В этом случае вам, возможно, придется немного почитать. знак равно

И да, я знаю, что что-то такое, как Photoshop, связывающее дерьмо с процессором, является проблемой. Другое (даже более сложное) решение состоит в том, чтобы вместо наличия сторожевого потока иметь отдельный сторожевой процесс, который имеет высший приоритет, поэтому он немного более защищен от привязки процессора. Но опять же, это становится действительно сложным.

Окончательное редактирование

Я собираюсь оставить все мои другие идеи выше для полноты картины, но кажется, что использование времени работы системы также будет правильным способом справиться с этим. поскольку [[NSProcessInfo processInfo] systemUptime] работает только в 10.6+, можно просто позвонить mach_absolute_time(), Чтобы получить доступ к этой функции, просто #include <mach/mach_time.h>, Это должно быть то же значение, что и возвращаемое NSProcessInfo,

Другие вопросы по тегам