Calculer le nombre de secondes entre deux instants dans Cocoa, même lorsque l'horloge système a changé à mi-chemin

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

Question

J'écris un programme destiné à l'utilisateur final de Cocoa OS X (Leopard 10.5+) qui utilise des horodatages pour calculer des statistiques sur la durée d'affichage d'un élément à l'écran. Le temps est calculé périodiquement pendant l'exécution du programme à l'aide d'un NSTimer répété. [NSDate date] permet de capturer des horodatages, Démarrer et Terminer . Calculer la différence entre les deux dates en secondes est simple.

Un problème survient si un utilisateur final ou ntp modifie l'horloge système. - (NSTimeInterval)systemUptime dépend de l'horloge système. Par conséquent, si elle est modifiée, la variable Terminer sera faussée par rapport à Démarrer , ce qui perturbera considérablement le calcul de l'heure. Ma question:

1. Comment puis-je calculer avec précision le temps entre Démarrer et Terminer , en secondes, même lorsque l'horloge système est modifiée à mi-chemin?

Je pense avoir besoin d'un point de référence inchangé pour pouvoir calculer le nombre de secondes écoulées depuis lors. Par exemple, la disponibilité du système. 10.6 a NSProcessInfo, une partie de <=>, qui fournit le temps de disponibilité du système. Toutefois, cela ne fonctionnera pas car mon application doit fonctionner dans la version 10.5.

J'ai essayé de créer un compteur de temps à l'aide de NSTimer, mais ce n'est pas exact. NSTimer a plusieurs modes d'exécution différents et ne peut en exécuter qu'un seul à la fois. NSTimer (par défaut) est placé dans le mode d'exécution par défaut . Si un utilisateur commence à manipuler l’UI pendant assez longtemps, ceci entrera dans NSEventTrackingRunLoopMode et ignorera le mode d’exécution par défaut , ce qui peut entraîner l’ignorance des déclenchements NSTimer. une manière inexacte de compter les secondes.

J'ai également envisagé de créer un thread séparé (NSRunLoop) pour exécuter un deuxième compteur NSTimer, le gardant ainsi à l'écart des interactions de l'interface utilisateur. Mais je suis très novice en multi-threading et je voudrais éviter cela si possible. De plus, je ne suis pas sûr que cela fonctionnerait correctement si le processeur était bloqué par une autre application (Photoshop générant une image de grande taille, etc.), ce qui ferait que mon NSRunLoop serait mis en attente assez longtemps pour gâcher son contenu. NSTimer.

J'apprécie toute aide. :)

Était-ce utile?

La solution 3

J'ai trouvé un moyen de le faire à l'aide de la fonction C UpTime () , fournie dans <CoreServices/CoreServices.h>. Cela renvoie le temps absolu (spécifique à la CPU), qui peut facilement être converti en durée (en millisecondes ou en nanosecondes). Détails ici: http://www.meandmark.com/timingpart1.html (voir sous la partie) 3 pour UpTime)

Je ne pouvais pas mach_absolute_time() fonctionner correctement, probablement à cause de mon manque de connaissances à ce sujet et de l'impossibilité de trouver beaucoup de documentation sur le Web à ce sujet. Il semble prendre le même temps que UpTime(), mais le convertir en un double me laisse abasourdi.

[[NSApp currentEvent] timestamp] a fonctionné, mais uniquement si l'application recevait NSEvents. Si l'application passait au premier plan, elle ne recevrait pas les événements et <=> continuerait simplement à renvoyer le même horodatage à plusieurs reprises dans une méthode d'activation NSTimer, jusqu'à ce que l'utilisateur final décide à nouveau d'interagir avec l'application.

Merci pour toute votre aide Marc et Mike! Vous m'avez certainement envoyé dans la bonne direction, menant à la réponse. :)

Autres conseils

En fonction de la nature de ce code, vous avez le choix entre 2:

  • Pour une précision absolue, utilisez mach_absolute_time() . Il donnera l'intervalle de temps exactement entre les points auxquels vous avez appelé la fonction.
  • Mais dans une application graphique, cela est souvent indésirable. Au lieu de cela, vous souhaitez connaître le décalage horaire entre les événements qui ont débuté et terminé votre durée. Si oui, comparez [[NSApp currentEvent] timestamp]

D'accord, c'est donc un long plan, mais vous pouvez essayer d'implémenter quelque chose comme NSSystemClockDidChangeNotification disponible dans Snow Leopard.

Alors, supportez-moi avec moi ici, car c’est une idée étrange et qui est définitivement non-dérterministe. Mais que se passe-t-il si vous avez un fil de surveillance tout au long de votre programme? Ce fil lisait l’heure du système toutes les n secondes et l’enregistrait. Par souci d'argumentation, il suffit de laisser 5 secondes. Ainsi, toutes les 5 secondes, il compare la lecture précédente à l'heure système actuelle. S'il y a un & Quot; assez grand & Quot; la différence (& "; assez grand &") aurait nécessairement besoin d'être supérieure à 5, mais pas trop , pour tenir compte du non déterminisme de la planification des processus et de la hiérarchisation des threads) , postez un avis indiquant qu’il ya eu un changement de temps important. Vous auriez besoin de jouer avec fuzzing la valeur qui constitue & "Assez grand &"; (ou assez petit, si l'horloge a été réinitialisée) pour vos besoins de précision.

Je sais que c’est un peu hacky, mais à moins d’une autre solution, qu’en pensez-vous? Est-ce que cela, ou quelque chose comme ça, pourrait résoudre votre problème?

Modifier

Très bien, vous avez modifié votre question initiale pour indiquer que vous préférez ne pas utiliser de fil de surveillance car vous débutez en multithreading. Je comprends la peur de faire quelque chose d'un peu plus avancé que ce que vous êtes à l'aise, mais cela pourrait être la seule solution. Dans ce cas, vous pourriez avoir un peu de lecture à faire. =)

Et oui, je sais qu’un problème tel que Photoshop est en train de faire sortir la merde du processeur. Une autre solution (encore plus compliquée) serait, au lieu d'avoir un fil de surveillance , , de disposer d'un processus de surveillance distinct prioritaire, de sorte qu'il soit un peu plus immunisé contre les processeurs. ancrage. Mais encore une fois, cela devient vraiment compliqué.

Modification finale

Je vais laisser toutes mes idées ci-dessus par souci de complétude, mais il semble que l'utilisation de la disponibilité du système sera également un moyen valable de traiter cette question. Etant donné que [[NSProcessInfo processInfo] systemUptime] ne fonctionne que dans 10.6+, vous pouvez simplement appeler mach_absolute_time(). Pour accéder à cette fonction, il suffit de #include <mach/mach_time.h>. Cela devrait être la même valeur que celle renvoyée par NSProcessInfo.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top