c++ - extraer - pasaje de milisegundos a minutos
Cómo hacer que el hilo duerma menos de un milisegundo en Windows (18)
En Windows tengo un problema que nunca encontré en Unix. Así es cómo hacer que un hilo duerma menos de un milisegundo. En Unix, normalmente tiene una serie de opciones (dormir, dormir y dormir) para satisfacer sus necesidades. En Windows, sin embargo, solo hay suspensión con granularidad de milisegundos.
En Unix, puedo utilizar el uso de la llamada al sistema de select
para crear un sueño de microsegundos, que es bastante sencillo:
int usleep(long usec)
{
struct timeval tv;
tv.tv_sec = usec/1000000L;
tv.tv_usec = usec%1000000L;
return select(0, 0, 0, 0, &tv);
}
¿Cómo puedo lograr lo mismo en Windows?
¿Qué estás esperando que requiere tal precisión? En general, si necesita especificar ese nivel de precisión (por ejemplo, debido a una dependencia de algún hardware externo), se encuentra en la plataforma incorrecta y debería considerar un sistema operativo en tiempo real.
De lo contrario, deberías considerar si hay un evento en el que puedas sincronizar o, en el peor de los casos, simplemente esperar la CPU y usar el contador de alto rendimiento API para medir el tiempo transcurrido.
Como dice Joel, no se puede ''dormir'' significativamente (es decir, renunciar a la CPU programada) por períodos tan cortos. Si desea retrasar durante un tiempo breve, debe girar, verificando repetidamente un temporizador de alta resolución adecuado (por ejemplo, el "temporizador de rendimiento") y esperando que algo de alta prioridad no lo impida de todos modos.
Si realmente le importan las demoras exactas de esos tiempos cortos, no debería usar Windows.
Como han señalado varias personas, el sueño y otras funciones relacionadas dependen por defecto del "tic del sistema". Esta es la unidad de tiempo mínima entre las tareas del sistema operativo; el programador, por ejemplo, no se ejecutará más rápido que esto. Incluso con un sistema operativo en tiempo real, el tic del sistema no suele ser inferior a 1 ms. Aunque es ajustable, esto tiene implicaciones para todo el sistema, no solo para la funcionalidad de suspensión, ya que su planificador se ejecutará con más frecuencia y posiblemente aumente la sobrecarga de su sistema operativo (cantidad de tiempo para que se ejecute el planificador, en comparación con la cantidad de tiempo de ejecución de una tarea).
La solución a esto es usar un dispositivo externo de reloj de alta velocidad. La mayoría de los sistemas Unix le permitirán especificar sus temporizadores y un reloj tan diferente para usar, a diferencia del reloj predeterminado del sistema.
Como todo el mundo mencionado, no hay garantías sobre el tiempo de sueño. Pero nadie quiere admitir que a veces, en un sistema inactivo, el comando usleep puede ser muy preciso. Especialmente con un kernel sin tick. Windows Vista lo tiene y Linux lo tiene desde 2.6.16.
Tickless kernels existe para ayudar a mejorar la vida útil de las computadoras portátiles: consulte la utilidad powertop de Intel.
En esa condición, sucedió que había medido el comando Linux usleep que respetaba el tiempo de espera solicitado muy de cerca, hasta media docena de micro segundos.
Por lo tanto, tal vez el OP desee algo que funcione más o menos la mayor parte del tiempo en un sistema inactivo, ¡y podrá solicitar una programación de micro segundos! De hecho, también me gustaría eso en Windows.
También Sleep (0) suena como boost :: thread :: yield (), cuya terminología es más clara.
Me pregunto si Boost -timed locks tiene una mejor precisión. Porque entonces solo puedes bloquear un mutex que nadie lanza, y cuando se alcanza el tiempo de espera, continúa ... Los tiempos de espera se establecen con boost :: system_time + boost :: milisegundos & cie (xtime está en desuso).
En Windows, el uso de select
te obliga a incluir la biblioteca Winsock que debe inicializarse así en tu aplicación:
WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
Y luego, la selección no le permitirá ser llamado sin ningún socket, por lo que debe hacer un poco más para crear un método microsleep:
int usleep(long usec)
{
struct timeval tv;
fd_set dummy;
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
FD_ZERO(&dummy);
FD_SET(s, &dummy);
tv.tv_sec = usec/1000000L;
tv.tv_usec = usec%1000000L;
return select(0, 0, 0, &dummy, &tv);
}
Todos estos métodos creados para el sueño regresan a cero cuando tienen éxito y no son cero para los errores.
En general, un sueño durará al menos hasta que ocurra la próxima interrupción del sistema. Sin embargo, esto depende de la configuración de los recursos del temporizador multimedia. Puede establecerse en algo cercano a 1 ms, incluso un hardware permite ejecutarse en periodos de interrupción de 0.9765625 ( ActualResolution provista por NtQueryTimerResolution
mostrará 0.9766 pero eso es realmente incorrecto. Simplemente no pueden poner el número correcto en el formato de resolución real . 0.9765625ms a 1024 interrupciones por segundo).
Hay una excepción que nos permite escapar del hecho de que puede ser imposible dormir menos que el período de interrupción: es el famoso Sleep(0)
. ¡Esta es una herramienta muy poderosa y no se usa con la frecuencia que debería! Renuncia al recordatorio del segmento de tiempo del hilo. De esta forma, el hilo se detendrá hasta que el programador obligue a la secuencia a obtener nuevamente el servicio de CPU. Sleep(0)
es un servicio asincrónico, la llamada obligará al programador a reaccionar independientemente de una interrupción.
Una segunda forma es el uso de un waitable object
. Una función de espera como WaitForSingleObject()
puede esperar un evento. Para tener un hilo durmiendo durante cualquier tiempo, también tiempos en el régimen de microsegundos, el hilo necesita configurar algún hilo de servicio que genere un evento con el retardo deseado. El hilo "dormido" configurará este hilo y luego pausará en la función de espera hasta que el hilo de servicio establezca el evento señalado.
De esta forma, cualquier hilo puede "dormir" o esperar en cualquier momento. El hilo de servicio puede ser de gran complejidad y puede ofrecer servicios de todo el sistema, como eventos temporizados con una resolución de microsegundos. Sin embargo, una resolución de microsegundos puede obligar a la cadena de servicio a girar en un servicio de tiempo de alta resolución durante, como máximo, un período de interrupción (~ 1 ms). Si se tiene cuidado, esto puede funcionar muy bien, particularmente en sistemas multiprocesador o multi-core. Un giro de un ms no duele considerablemente en el sistema de núcleos múltiples, cuando la máscara de afinidad para el hilo de llamada y el hilo de servicio se manejan con cuidado.
El código, la descripción y las pruebas se pueden visitar en el Windows Timestamp Project
En realidad, el uso de esta función de reposo provocará una gran fuga de memoria / recursos. (dependiendo de la frecuencia de llamada)
use esta versión corregida (lo siento, ¿no puede editar?)
bool usleep(unsigned long usec)
{
struct timeval tv;
fd_set dummy;
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
FD_ZERO(&dummy);
FD_SET(s, &dummy);
tv.tv_sec = usec / 1000000ul;
tv.tv_usec = usec % 1000000ul;
bool success = (0 == select(0, 0, 0, &dummy, &tv));
closesocket(s);
return success;
}
Esto indica un mal entendimiento de las funciones de sueño. El parámetro que pasa es un tiempo mínimo para dormir. No hay garantía de que el hilo se active después de exactamente el tiempo especificado. De hecho, los hilos no se "despiertan" en absoluto, sino que son elegidos para ser ejecutados por el planificador. El planificador puede optar por esperar mucho más tiempo que la duración de reposo solicitada para activar un hilo, especialmente si otro hilo todavía está activo en ese momento.
Intenta usar SetWaitableTimer ...
Pruebe boost :: xtime y timed_wait ()
tiene precisión de nanosegundos.
Sí, necesita comprender los tiempos cuánticos de su sistema operativo. En Windows, ni siquiera obtendrá 1 ms de resolución a menos que cambie el tiempo de 1 ms. (Usando por ejemplo timeBeginPeriod () / timeEndPeriod ()) Eso todavía no garantiza nada. Incluso una pequeña carga o un solo controlador de dispositivo de mierda arrojará todo.
SetThreadPriority () ayuda, pero es bastante peligroso. Los malos controladores de dispositivo aún pueden arruinarlo.
Necesita un entorno de cómputo ultracontrolado para hacer que esto feo funcione.
Si desea tanta granularidad, se encuentra en el lugar equivocado (en espacio de usuario).
Recuerde que si se encuentra en el espacio de usuario, su tiempo no siempre es preciso.
El planificador puede iniciar su hilo (o aplicación) y programarlo, por lo que depende del planificador del sistema operativo.
Si busca algo preciso, tiene que ir: 1) En el espacio del kernel (como los controladores) 2) Elija un RTOS.
De todos modos, si está buscando cierta granularidad (pero recuerde el problema con el espacio de usuario), busque la función QueryPerformanceCounter y la función QueryPerformanceFrequency en MSDN.
Si tu objetivo es "esperar un tiempo muy corto" porque estás haciendo un spinwait , entonces hay niveles crecientes de espera que puedes realizar.
void SpinOnce(ref Int32 spin)
{
/*
SpinOnce is called each time we need to wait.
But the action it takes depends on how many times we''ve been spinning:
1..12 spins: spin 2..4096 cycles
12..32: call SwitchToThread (allow another thread ready to go on time core to execute)
over 32 spins: Sleep(0) (give up the remainder of our timeslice to any other thread ready to run, also allows APC and I/O callbacks)
*/
spin += 1;
if (spin > 32)
Sleep(0); //give up the remainder of our timeslice
else if (spin > 12)
SwitchTothread(); //allow another thread on our CPU to have the remainder of our timeslice
else
{
int loops = (1 << spin); //1..12 ==> 2..4096
while (loops > 0)
loops -= 1;
}
}
Entonces, si su objetivo es esperar solo un poco , puede usar algo como:
int spin = 0;
while (!TryAcquireLock())
{
SpinOne(ref spin);
}
La virtud aquí es que esperamos más tiempo cada vez, finalmente nos dormimos por completo.
Solo usa dormir (0). 0 es claramente menos de un milisegundo. Ahora, eso suena gracioso, pero lo digo en serio. Sleep (0) le dice a Windows que no tiene nada que hacer en este momento, pero que desea ser reconsiderado tan pronto como el planificador se ejecute de nuevo. Y dado que obviamente el hilo no puede programarse para ejecutarse antes de que se ejecute el programador, este es el retraso más breve posible.
Tenga en cuenta que puede pasar un número de microsegundo a su ususuario, pero también lo hace el nómero de inactividad (__ int64 t) {Sueño (t / 1000); } - no hay garantías para realmente dormir ese período.
Tengo el mismo problema y nada parece ser más rápido que un ms, incluso el sueño (0). Mi problema es la comunicación entre un cliente y una aplicación de servidor donde utilizo la función _InterlockedExchange para probar y configurar un bit y luego me Duermo (0).
Realmente necesito realizar miles de operaciones por segundo de esta manera y no funciona tan rápido como lo planeé.
Como tengo un cliente ligero que trata con el usuario, que a su vez invoca a un agente que luego habla con un hilo, pronto moveré el hilo con el agente para que no se requiera ninguna interfaz de evento.
Solo para darles una idea de lo lento que es este Sueño, realicé una prueba durante 10 segundos realizando un ciclo vacío (obteniendo algo así como 18,000,000 bucles) mientras que con el evento en el lugar solo obtuve 180,000 bucles. Es decir, ¡100 veces más lento!
Use los temporizadores de alta resolución disponibles en winmm.lib. Mira this para un ejemplo.
Función de suspensión que es mucho menos que un milisegundo, tal vez
Descubrí que el sueño (0) funcionó para mí. En un sistema con una carga cercana al 0% en la CPU en el administrador de tareas, escribí un programa de consola simple y la función de suspensión (0) durmió durante 1-3 microsegundos consistentes, que es mucho menos que un milisegundo.
Pero de las respuestas anteriores en este hilo, sé que la cantidad de sueño (0) puede variar mucho más salvajemente que esto en sistemas con una gran carga de CPU.
Pero, según tengo entendido, la función dormir no debe usarse como un temporizador. Se debe usar para hacer que el programa use el menor porcentaje posible de la CPU y ejecute con la mayor frecuencia posible. Para mis propósitos, como mover un proyectil a través de la pantalla en un videojuego mucho más rápido que un píxel por milisegundo, el sueño (0) funciona, creo.
Simplemente se aseguraría de que el intervalo de sueño sea mucho más pequeño que la mayor cantidad de tiempo que durmiera. No utiliza el modo de espera como temporizador, sino solo para que el juego use la cantidad mínima de porcentaje de CPU posible. Utilizaría una función separada que no tiene nada que ver es dormir para saber cuándo ha pasado una cantidad de tiempo determinada y luego mover el proyectil un píxel por la pantalla, en un tiempo de, por ejemplo, 1/10 de milisegundo o 100 microsegundos. .
El pseudo-código sería algo como esto.
while (timer1 < 100 microseconds) {
sleep(0);
}
if (timer2 >=100 microseconds) {
move projectile one pixel
}
//Rest of code in iteration here
Sé que la respuesta puede no funcionar para problemas o programas avanzados, pero puede funcionar para algunos o muchos programas.
#include <Windows.h>
static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");
static void SleepShort(float milliseconds) {
static bool once = true;
if (once) {
ULONG actualResolution;
ZwSetTimerResolution(1, true, &actualResolution);
once = false;
}
LARGE_INTEGER interval;
interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
NtDelayExecution(false, &interval);
}
sí, usa algunas funciones del kernel no documentadas, pero funciona muy bien, uso SleepShort (0.5); en algunos de mis muros