Calcular el número de segundos entre dos puntos en el tiempo, en Cocoa, incluso cuando el reloj del sistema ha cambiado a mitad de camino

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

Pregunta

Estoy escribiendo un programa de usuario final Cocoa OS X (Leopard 10.5+) que usa marcas de tiempo para calcular estadísticas de cuánto tiempo se muestra algo en la pantalla. El tiempo se calcula periódicamente mientras el programa se ejecuta utilizando un NSTimer repetitivo. [NSDate date] se utiliza para capturar marcas de tiempo, Inicio y Finalizar . Calcular la diferencia entre las dos fechas en segundos es trivial.

Se produce un problema si un usuario final o ntp cambia el reloj del sistema. - (NSTimeInterval)systemUptime se basa en el reloj del sistema, por lo que si se cambia, la variable Finish se sesgará en relación con el Start , lo que desordenará significativamente el cálculo del tiempo. Mi pregunta:

1. ¿Cómo puedo calcular con precisión el tiempo entre Inicio y Finalizar , en segundos, incluso cuando el reloj del sistema se cambia a mitad de camino?

Estoy pensando que necesito un punto de referencia que no cambie en el tiempo para poder calcular cuántos segundos han pasado desde entonces. Por ejemplo, tiempo de actividad del sistema. 10.6 tiene NSProcessInfo, parte de <=>, que proporciona tiempo de actividad del sistema. Sin embargo, esto no funcionará ya que mi aplicación debe funcionar en 10.5.

He intentado crear un contador de tiempo usando NSTimer, pero esto no es exacto. NSTimer tiene varios modos de ejecución diferentes y solo puede ejecutarse uno a la vez. NSTimer (por defecto) se pone en el modo de ejecución predeterminado . Si un usuario comienza a manipular la interfaz de usuario durante un tiempo suficientemente largo, esto ingresará a NSEventTrackingRunLoopMode y omitirá el modo de ejecución predeterminado , lo que puede provocar que se omitan los disparos de NSTimer, lo que hace que una forma inexacta de contar segundos.

También he pensado en crear un hilo separado (NSRunLoop) para ejecutar un segundo contador NSTimer, manteniéndolo alejado de las interacciones de la interfaz de usuario. Pero soy muy nuevo en multi-threading y me gustaría alejarme de eso si es posible. Además, no estoy seguro de si esto funcionaría con precisión en el caso de que la CPU se vincule con otra aplicación (Photoshop renderiza una imagen grande, etc.), haciendo que mi NSRunLoop quede en espera durante el tiempo suficiente para estropearlo NSTimer.

Agradezco cualquier ayuda. :)

¿Fue útil?

Solución 3

Descubrí una forma de hacer esto usando la función UpTime () C, proporcionada en <CoreServices/CoreServices.h>. Esto devuelve Tiempo absoluto (específico de la CPU), que se puede convertir fácilmente en Tiempo de duración (milisegundos o nanosegundos). Detalles aquí: http://www.meandmark.com/timingpart1.html (mire debajo de la parte 3 para UpTime)

No pude lograr que mach_absolute_time() funcionara correctamente, probablemente debido a mi falta de conocimiento al respecto, y al no poder encontrar mucha documentación en la web al respecto. Parece que toma el mismo tiempo que UpTime(), pero convertirlo en un doble me dejó estupefacto.

[[NSApp currentEvent] timestamp] funcionó, pero solo si la aplicación estaba recibiendo NSEvents. Si la aplicación entrara en primer plano, no recibiría eventos, y <=> simplemente continuaría devolviendo la misma marca de tiempo una y otra vez en un método de disparo NSTimer, hasta que el usuario final decidiera interactuar con la aplicación nuevamente.

¡Gracias por toda su ayuda Marc y Mike! Los dos definitivamente me enviaron en la dirección correcta que conduce a la respuesta. :)

Otros consejos

Dependiendo de lo que impulse este código, tiene 2 opciones:

  • Para una precisión absoluta, use mach_absolute_time() . Le dará el intervalo de tiempo exactamente entre los puntos en los que llamó a la función.
  • Pero en una aplicación GUI, esto a menudo es realmente indeseable. En cambio, desea la diferencia horaria entre los eventos que comenzaron y terminaron su duración. Si es así, compare [[NSApp currentEvent] timestamp]

Bien, esta es una posibilidad remota, pero podría intentar implementar algo así como NSSystemClockDidChangeNotification disponible en Snow Leopard.

Así que tengan paciencia conmigo aquí, porque esta es una idea extraña y definitivamente no es desrterminista. Pero, ¿y si tuvieras un hilo de vigilancia ejecutándose durante la duración de tu programa? Este hilo leería, cada n segundos, la hora del sistema y la almacenaría. En aras de la discusión, hagamos 5 segundos. Entonces, cada 5 segundos, compara la lectura anterior con la hora actual del sistema. Si hay un & Quot; suficientemente grande & Quot; diferencia (" suficientemente grande " definitivamente debería ser mayor que 5, pero no demasiado mayor, para tener en cuenta el no determinismo de la programación de procesos y la priorización de hilos) , publique una notificación de que ha habido un cambio de hora significativo. Tendría que jugar con difuminar el valor que constituye & Quot; suficientemente grande & Quot; (o lo suficientemente pequeño, si el reloj se restableció a una hora anterior) para sus necesidades de precisión.

Sé que esto es un poco raro, pero salvo cualquier otra solución, ¿qué te parece? ¿Podría eso, o algo así, resolver su problema?

Editar

Bien, entonces modificó su pregunta original para decir que preferiría no usar un hilo de vigilancia porque es nuevo en multihilo. Entiendo el miedo a hacer algo un poco más avanzado de lo que te sientes cómodo, pero esta podría ser la única solución. En ese caso, es posible que tenga que leer un poco. =)

Y sí, sé que algo como Photoshop que saca la basura del procesador es un problema. Otra solución (aún más complicada) sería, en lugar de tener un thread de watchdog, tener un proceso de watchdog separado que tenga la máxima prioridad, por lo que es un poco más inmune al procesador vinculación Pero de nuevo, esto se está volviendo realmente complicado.

Edición final

Voy a dejar todas mis otras ideas anteriores por completo, pero parece que usar el tiempo de actividad del sistema también será una forma válida de lidiar con esto. Como [[NSProcessInfo processInfo] systemUptime] solo funciona en 10.6+, solo puede llamar a mach_absolute_time(). Para obtener acceso a esa función, solo #include <mach/mach_time.h>. Ese debería ser el mismo valor devuelto por NSProcessInfo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top