c++ winapi sleep

sleep c++



¿Por qué el sueño(500) cuesta más de 500 ms? (7)

getTickCount() Sleep(500) en mi código y usé getTickCount() para probar el tiempo. Descubrí que tiene un costo de aproximadamente 515 ms, más de 500. ¿Alguien sabe por qué?


Como han señalado las otras respuestas, Sleep() tiene una precisión limitada. En realidad, ninguna implementación de una función similar a Sleep() puede ser perfectamente precisa, por varias razones:

  • Lleva algún tiempo llamar a Sleep() . Si bien una implementación que apunta a la máxima precisión podría intentar medir y compensar esta sobrecarga, pocos se molestan. (Y, en cualquier caso, la sobrecarga puede variar debido a muchas causas, incluido el uso de CPU y memoria).

  • Incluso si el temporizador subyacente utilizado por Sleep() dispara exactamente a la hora deseada, no hay garantía de que su proceso se reprograme inmediatamente después de despertarse. Es posible que su proceso se haya intercambiado mientras dormía, u otros procesos podrían estar acaparando la CPU.

  • Es posible que el sistema operativo no pueda activar su proceso a la hora solicitada, por ejemplo, porque la computadora está en modo de suspensión. En tal caso, es muy posible que su llamada Sleep() 500 ms termine en realidad varias horas o días.

Además, incluso si Sleep() fuera perfectamente preciso, el código que desea ejecutar después de dormir inevitablemente consumirá algo de tiempo extra. Por lo tanto, para realizar alguna acción (por ejemplo, volver a dibujar la pantalla o actualizar la lógica del juego) a intervalos regulares, la solución estándar es usar un bucle Sleep() compensado . Es decir, usted mantiene un contador de tiempo de incremento regular que indica cuándo debe ocurrir la siguiente acción, y compara este tiempo objetivo con el tiempo actual del sistema para ajustar dinámicamente su tiempo de sueño.

Se debe tener un cuidado especial para lidiar con saltos inesperados de gran tiempo, por ejemplo, si se sospechó temporalmente de la computadora o si el contador de tics se envolvió, así como la situación en la que el procesamiento de la acción termina tomando más tiempo del que está disponible antes del próximo acción, haciendo que el bucle se quede atrás.

Aquí hay un ejemplo rápido de implementación (en pseudocódigo) que debería manejar estos dos problemas:

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(); }

Esto garantizará que doAction() se doAction() en promedio una vez cada interval milisegundos, al menos siempre que no consuma constantemente más tiempo que eso, y siempre que no se produzcan saltos de tiempo grandes. El tiempo exacto entre llamadas sucesivas puede variar, pero dicha variación se compensará en la próxima interacción.


Como puede leer en la documentación , la función GetTickCount()

está limitado a la resolución del temporizador del sistema, que generalmente está en el rango de 10 milisegundos a 16 milisegundos.

Para obtener una medición de tiempo más precisa, use la función GetSystemDatePreciseAsFileTime

Además, no puede confiar en Sleep(500) para dormir exactamente 500 milisegundos. Suspenderá el hilo durante al menos 500 milisegundos. El sistema operativo continuará el hilo tan pronto como tenga un intervalo de tiempo disponible. Cuando hay muchas otras tareas ejecutándose en el sistema operativo, puede haber un retraso.


En general, dormir significa que su hilo pasa a un estado de espera y después de 500 ms estará en un estado "ejecutable". Luego, el planificador del sistema operativo elige ejecutar algo de acuerdo con la prioridad y el número de procesos ejecutables en ese momento. Entonces, si tiene un sueño de alta precisión y un reloj de alta precisión, entonces todavía es un sueño de al menos 500 ms, no exactamente 500 ms.


Hay dos razones generales por las que el código puede querer una función como "dormir":

  1. Tiene alguna tarea que se puede realizar en cualquier momento que esté al menos a cierta distancia en el futuro.

  2. Tiene alguna tarea que debe realizarse lo más cerca posible de algún momento a cierta distancia en el futuro.

En un buen sistema, debe haber formas separadas de emitir ese tipo de solicitudes; Windows hace que el primero sea más fácil que el segundo.

Supongamos que hay una CPU y tres subprocesos en el sistema, todos haciendo un trabajo útil hasta que, un segundo antes de la medianoche, uno de los subprocesos dice que no tendrá nada útil que hacer durante al menos un segundo. En ese punto, el sistema dedicará la ejecución a los dos subprocesos restantes. Si, 1 ms antes de la medianoche, uno de esos subprocesos decide que no tendrá nada útil que hacer durante al menos un segundo, el sistema cambiará el control al último subproceso restante.

Cuando llegue la medianoche, el primer subproceso original estará disponible para ejecutarse, pero dado que el subproceso que se está ejecutando actualmente solo habrá tenido la CPU durante un milisegundo en ese momento, no hay ninguna razón particular para que el primer subproceso original deba considerarse más "digno" de tiempo de CPU que el otro hilo que acaba de obtener el control. Dado que el cambio de subprocesos no es gratuito, el sistema operativo puede muy bien decidir que el subproceso que actualmente tiene la CPU debe mantenerlo hasta que se bloquee en algo o se haya agotado un segmento de tiempo completo.

Sería bueno si hubiera una versión de "suspensión" que fuera más fácil de usar que los temporizadores multimedia, pero que solicitaría que el sistema otorgue al hilo un impulso de prioridad temporal cuando sea elegible para ejecutarse nuevamente, o mejor aún, una variación de "dormir", que especificaría un tiempo mínimo y un tiempo de "aumento de prioridad", para las tareas que deben realizarse dentro de un período de tiempo determinado. Sin embargo, no conozco ningún sistema que pueda funcionar fácilmente de esa manera.


La resolución predeterminada del temporizador es baja, puede aumentar la resolución del tiempo si es necesario. MSDN

#define TARGET_RESOLUTION 1 // 1-millisecond target resolution TIMECAPS tc; UINT wTimerRes; if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { // Error; application can''t continue. } wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); timeBeginPeriod(wTimerRes);


Porque la Sleep Win32 API no es una Sleep alta precisión y tiene una granularidad máxima.

La mejor manera de obtener un sueño de precisión es dormir un poco menos (~ 50 ms) y hacer una espera ocupada. Para encontrar la cantidad exacta de tiempo que necesita para esperar, obtenga la resolución del reloj del sistema usando timeGetDevCaps y multiplique por 1.5 o 2 para estar seguro.


sleep(500) garantiza un sueño de al menos 500 ms.

Pero podría dormir más tiempo que eso: el límite superior no está definido.

En su caso, también habrá una sobrecarga adicional al llamar a getTickCount() .

Su función de Sleep no estándar puede comportarse de manera diferente; pero dudo que la exactitud esté garantizada. Para hacer eso, necesitas un hardware especial.