53 votes

Pourquoi Sleep (500) coûte plus de 500 ms?

J'ai utilisé Sleep(500) dans mon code et j'ai utilisé getTickCount() pour tester le timing. J'ai trouvé qu'il en coûte environ 515 ms, plus de 500. Quelqu'un sait-il pourquoi?

63voto

nightcracker Points 34498

Parce que Sleep API Win32 n'est pas une veille de haute précision et a une granularité maximale.

Le meilleur moyen d'obtenir un sommeil de précision est de dormir un peu moins (~ 50 ms) et d'attendre. Pour trouver le temps exact dont vous avez besoin pour attendre, obtenez la résolution de l'horloge système à l'aide de timeGetDevCaps et multipliez-la par 1,5 ou 2 pour plus de sécurité.

30voto

Bathsheba Points 23209

sleep(500) garantit un sommeil d' au moins 500 ms.

Mais il peut dormir plus longtemps que cela: la limite supérieure n'est pas définie.

Dans votre cas, il y aura également des frais généraux supplémentaires liés à l'appel de getTickCount() .

Votre fonction Sleep non standard peut fort bien se comporter différemment; mais je doute que l'exactitude soit garantie. Pour ce faire, vous avez besoin d'un matériel spécial.

18voto

Philipp Points 22441

Comme vous pouvez le lire dans la documentation, la WinAPI fonction GetTickCount()

est limitée à la résolution de l'horloge du système, qui est généralement de l'ordre de 10 millisecondes à 16 millisecondes.

Pour obtenir une mesure plus précise de la mesure du temps, utilisez la fonction GetSystemDatePreciseAsFileTime

Aussi, vous ne pouvez pas compter sur Sleep(500) dormir exactement 500 millisecondes. Il suspend le thread pour au moins 500 millisecondes. Le système d'exploitation va ensuite continuer le fil dès qu'il a un créneau horaire disponible. Quand il y a beaucoup d'autres tâches en cours d'exécution sur le système d'exploitation, il peut y avoir un délai.

13voto

Notinlist Points 3060

En général, dormir signifie que votre thread passe à un état d'attente et au bout de 500 ms, il sera dans un état "exécutable". Ensuite, le planificateur de système d'exploitation choisit d'exécuter quelque chose en fonction de la priorité et du nombre de processus exécutables à ce moment. Donc, si vous avez un sommeil de haute précision et une horloge de haute précision, il s'agit toujours d'un sommeil d'au moins 500 ms, pas exactement de 500 ms.

12voto

Ilmari Karonen Points 20585

Comme les autres réponses ont noté, Sleep() a une précision limitée. En fait, pas de la mise en œuvre d'un Sleep()-comme la fonction peut être tout à fait précis, pour plusieurs raisons:

  • Il faut un certain temps à réclamer Sleep(). Alors qu'une mise en œuvre visant pour un maximum de précision pourrait tenter de mesurer et de compenser cette surcharge de travail, peu de peine. (Et, dans tous les cas, les frais généraux peuvent varier en raison de plusieurs causes, y compris les CPU et de mémoire.)

  • Même si le sous-jacent minuterie utilisée par Sleep() des incendies exactement à l'heure souhaitée, il n'y a aucune garantie que votre processus sera effectivement reporté immédiatement après le réveil. Votre processus ont été échangés pendant qu'il était en train de dormir, ou d'autres processus pourraient être monopolisant le CPU.

  • Il est possible que le système d'exploitation ne peut pas réveiller votre processus jusqu'à l'heure demandée, par exemple, parce que l'ordinateur est en mode suspension. Dans un tel cas, il est tout à fait possible que votre 500ms Sleep() appel se fait prendre plusieurs heures ou plusieurs jours.

Aussi, même si Sleep() était parfaitement exacte, le code que vous voulez exécuter après le sommeil va inévitablement consommer un peu plus de temps. Ainsi, pour réaliser une action (par exemple, redessiner l'écran, ou la mise à jour de logique de jeu) à intervalles réguliers, la solution courante est d'utiliser un compensée Sleep() boucle. Qui est, vous devez maintenir régulièrement l'incrémentation de compteur de temps indiquant quand la prochaine action doit se produire, et de comparer cette cible de temps avec le système actuel de temps pour ajuster de manière dynamique votre temps de sommeil.

Des soins supplémentaires doivent être prises pour faire face aux imprévus grand saut dans le temps, par exemple si l'ordinateur a été temporairement suspectée ou si le compteur de cycles enroulé autour, ainsi que de la situation où le traitement de l'action finit par prendre plus de temps que ce qui est disponible avant la prochaine action, provoquant la boucle à la traîne.

Voici un exemple rapide de mise en oeuvre (pseudo-code) qui doit gérer à la fois de ces questions:

int interval = 500, giveUpThreshold = 10*interval;
int nextTarget = GetTickCount();

bool active = doAction();
while (active) {
    nextTarget += interval;
    int delta = nextTarget - GetTickCount();
    if (delta > giveUpThreshold || delta < -giveUpThreshold) {
        // either we're hopelessly behind schedule, or something
        // weird happened; either way, give up and reset the target
        nextTarget = GetTickCount();
    } else if (delta > 0) {
        Sleep(delta);
    }
    active = doAction();
}

Cela permettra d'assurer que doAction() sera appelé en moyenne une fois tous les interval millisecondes, au moins tant qu'il n'est pas toujours consommer plus de temps, et tant que pas grand saut dans le temps, se produire. L'heure exacte entre les appels successifs peuvent varier, mais ces différences seront indemnisés sur la prochaine interaction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X