Pregunta

En Windows tengo un problema que nunca encontré en Unix.Así es como hacer que un hilo entre en suspensión durante menos de un milisegundo.En Unix normalmente tienes varias opciones (sleep, usleep y nanosleep) que se adaptan a tus necesidades.En Windows, sin embargo, sólo hay Dormir con granularidad de milisegundos.

En Unix, puedo usar el uso de select llamada al sistema 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?

¿Fue útil?

Solución 18

En Windows el uso de select te obliga a incluir el winsock biblioteca que debe inicializarse así en su aplicación:

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);

Y luego la selección no permitirá que te llamen sin ningún socket, por lo que tendrás que hacer un poco más para crear un método de microsueño:

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 usleep creados devuelven cero cuando tienen éxito y un valor distinto de cero en caso de errores.

Otros consejos

Esto indica una mala comprensión de las funciones del sueño.El parámetro que pasas es un mínimo tiempo para dormir.No hay garantía de que el hilo se active exactamente después del tiempo especificado.De hecho, los subprocesos no se "despiertan" en absoluto, sino que el programador los elige para su ejecución.El programador puede optar por esperar mucho más tiempo que la duración de suspensión solicitada para activar un subproceso, especialmente si otro subproceso todavía está activo en ese momento.

Como dice Joel, no se puede "dormir" significativamente (es decir,renunciar a su CPU programada) durante períodos tan cortos.Si desea retrasar un poco de tiempo, entonces necesita girar, verificando repetidamente un temporizador de alta resolución adecuado (p. ej.el 'temporizador de rendimiento') y esperando que algo de alta prioridad no se apodere de usted de todos modos.

Si realmente le importan los retrasos precisos en tiempos tan cortos, no debería utilizar Windows.

Utilice los temporizadores de alta resolución disponibles en winmm.lib.Ver este para un ejemplo.

Sí, necesita comprender los cuantos de tiempo de su sistema operativo.En Windows, ni siquiera obtendrá tiempos de resolución de 1 ms a menos que cambie el cuanto de tiempo a 1 ms.(Usando, por ejemplo, timeBeginPeriod()/timeEndPeriod()) Eso todavía no garantiza nada.Incluso una pequeña carga o un único controlador de dispositivo deficiente arruinará todo.

SetThreadPriority() ayuda, pero es bastante peligroso.Los controladores de dispositivos defectuosos aún pueden arruinarte.

Se necesita un entorno informático ultracontrolado para que estas cosas feas funcionen.

#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í, utiliza algunas funciones del kernel no documentadas, pero funciona muy bien, yo uso SleepShort(0.5);en algunos de mis hilos

Si desea tanta granularidad, está en el lugar equivocado (en el espacio de usuario).

Recuerda que si estás en el espacio de usuario tu tiempo no siempre es preciso.

El programador puede iniciar su hilo (o aplicación) y programarlo, por lo que usted depende del programador del sistema operativo.

Si buscas algo preciso tienes que ir:1) En el espacio del núcleo (como los controladores) 2) Elija un RTOS.

De todos modos, si está buscando algo de granularidad (pero recuerde el problema con el espacio del usuario), busque la función de consulta de consulta y la función de consulta de consulta en MSDN.

Como han señalado varias personas, el sueño y otras funciones relacionadas dependen por defecto del "tick del sistema".Ésta es la unidad mínima de tiempo entre 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.Si bien se puede ajustar, esto tiene implicaciones para todo el sistema, no solo para la funcionalidad de suspensión, porque su programador se ejecutará con más frecuencia y potencialmente aumentará la sobrecarga de su sistema operativo (cantidad de tiempo para que el programador se ejecute, vs.cantidad de tiempo que puede ejecutarse una tarea).

La solución a esto es utilizar un dispositivo de reloj externo de alta velocidad.La mayoría de los sistemas Unix le permitirán especificar sus temporizadores y un reloj diferente a usar, a diferencia del reloj predeterminado del sistema.

¿A qué esperas que requiera tanta precisión?En general si tu necesidad para especificar ese nivel de precisión (p. ej.debido a la dependencia de algún hardware externo) está en la plataforma incorrecta y debe buscar un sistema operativo en tiempo real.

De lo contrario, debería considerar si hay un evento en el que pueda sincronizar o, en el peor de los casos, simplemente espere la CPU y use la API de contador de alto rendimiento para medir el tiempo transcurrido.

Generalmente una suspensión durará al menos hasta que ocurra la siguiente interrupción del sistema.Sin embargo, esto depende de la configuración de los recursos del temporizador multimedia.Se puede establecer en algo cercano a 1 ms, algún hardware incluso permite ejecutarse en períodos de interrupción de 0.9765625 (Resolución real proporcionado por NtQueryTimerResolution mostrará 0.9766 pero eso en realidad está mal.Simplemente no pueden poner el número correcto en el Resolución real formato.Son 0,9765625 ms a 1024 interrupciones por segundo).

Hay una excepción que nos permite escapar del hecho de que puede ser imposible dormir por menos del período de interrupción:es el famoso Sleep(0).¡Esta es una herramienta muy poderosa y no se usa tan a menudo como debería!Renuncia al recordatorio del intervalo de tiempo del hilo.De esta manera, el subproceso se detendrá hasta que el programador lo obligue a obtener el servicio de la CPU nuevamente. Sleep(0) Es un servicio asincrónico, la llamada obligará al planificador 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 que un hilo esté inactivo en cualquier momento, también en el régimen de microsegundos, el hilo necesita configurar algún hilo de servicio que generará un evento con el retraso deseado.El subproceso "durmiente" configurará este subproceso y luego se detendrá en la función de espera hasta que el subproceso 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 para todo el sistema, como eventos cronometrados con una resolución de microsegundos.Sin embargo, la resolución de microsegundos puede obligar al subproceso del 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 múltiples núcleos.Un giro de un ms no perjudica considerablemente en un sistema multinúcleo, 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 Proyecto de marca de tiempo de Windows

Tengo el mismo problema y nada parece ser más rápido que un ms, incluso Sleep(0).Mi problema es la comunicación entre una aplicación cliente y servidor donde uso la función _InterlockedExchange para probar y configurar un poco y luego duermo (0).

Realmente necesito realizar miles de operaciones por segundo de esta manera y no funciona tan rápido como planeé.

Dado que tengo un cliente ligero que trata con el usuario, que a su vez invoca a un agente que luego habla con un hilo, pronto fusionaré el hilo con el agente para que no se requiera ninguna interfaz de eventos.

Sólo para darles una idea de lo lento que es este modo de suspensión, realicé una prueba durante 10 segundos realizando un bucle vacío (obteniendo algo así como 18.000.000 de bucles), mientras que con el evento en marcha solo obtuve 180.000 bucles.Es decir, ¡100 veces más lento!

En realidad, el uso de esta función usleep provocará una gran pérdida de memoria/recursos.(dependiendo de la frecuencia con la que se llame)

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

Como todo el mundo ha 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 núcleo sin cosquillas.Windows Vista lo tiene y Linux lo tiene desde 2.6.16.

Los núcleos Tickless existen para ayudar a mejorar la duración de la batería de las computadoras portátiles:cf.La utilidad powertop de Intel.

En esa condición, medí el comando usleep de Linux que respetaba muy de cerca el tiempo de suspensión solicitado, hasta media docena de microsegundos.

Entonces, tal vez el OP quiera algo que funcione aproximadamente la mayor parte del tiempo en un sistema inactivo y que pueda solicitar una programación de microsegundos.De hecho, también me gustaría eso en Windows.

Además, Sleep(0) suena como boost::thread::yield(), cuya terminología es más clara.

Me pregunto si Aumentar-Las cerraduras temporizadas tienen una mayor precisión.Porque entonces podrías simplemente bloquear un mutex que nadie libera nunca, y cuando se alcance el tiempo de espera, continuar...Los tiempos de espera se establecen con boost::system_time + boost::millisegundos & cie (xtime está en desuso).

Pruebe boost::xtime y timed_wait()

tiene una precisión de nanosegundos.

Simplemente use 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 que lo reconsideren tan pronto como el programador se ejecute nuevamente.Y dado que obviamente no se puede programar la ejecución del hilo antes de que se ejecute el programador, este es el retraso más corto posible.

Tenga en cuenta que puede pasar un número de microsegundos a su usleep, pero también lo hace void usleep(__int64 t) { Sleep(t/1000);} - no hay garantías de dormir ese período.

Función de sueño que dura mucho menos de un milisegundo, tal vez

Descubrí que dormir (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 a 3 microsegundos constantes, que es mucho menos de un milisegundo.

Pero a partir de las respuestas anteriores en este hilo, sé que la cantidad de sueño (0) puede variar mucho más que esto en sistemas con una gran carga de CPU.

Pero según tengo entendido, la función de suspensión no debe utilizarse como temporizador.Debe usarse para que el programa utilice el menor porcentaje posible de la CPU y se 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, creo que la suspensión (0) funciona.

Simplemente asegúrese de que el intervalo de sueño sea mucho menor que la mayor cantidad de tiempo que dormiría.No usas la suspensión como temporizador, sino solo para que el juego use la mínima cantidad de porcentaje de CPU posible.Usaría una función separada que no tiene nada que ver con dormir para saber cuándo ha pasado una cantidad de tiempo particular y luego mover el proyectil un píxel a través de la pantalla, en un tiempo de, digamos, 1/10 de milisegundo o 100 microsegundos. .

El pseudocó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 es posible que la respuesta no funcione para problemas o programas avanzados, pero puede funcionar para algunos o muchos programas.

Si tu objetivo es "esperar por un período de tiempo muy corto" porque estás haciendo un giraresperar, 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 tu objetivo es realmente esperar solo por un poquito, puedes usar algo como:

int spin = 0;
while (!TryAcquireLock()) 
{ 
   SpinOne(ref spin);
}

La virtud aquí es que cada vez esperamos más y finalmente nos quedamos completamente dormidos.

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