¿Es el sueño () malvado?

En primer lugar, hay muchos casos en los que se utiliza indebidamente Sleep() , por ejemplo, para “sincronizar” subprocesos o para sondear regularmente un valor donde una función de notificación lo haría (en Win32 WaitForSingleObject por ejemplo)

¿Pero qué pasa con otros casos de uso? ¿El Sleep siempre es malo? Si no, ¿cuáles son los buenos casos de uso para Sleep ? En caso afirmativo, ¿por qué casi todos los idiomas tienen algún tipo de statement de Sleep ?

PD: He hecho esta pregunta debido a uno de los comentarios de otra pregunta. Allí, el OP dice que, en su opinión, Sleep debe evitarse como goto .

De hecho, creo que la afirmación que vinculó es correcta. El problema es que el sueño se está utilizando (como se señaló) como un sustituto ineficiente de los mecanismos de notificación. El sueño siempre es inferior a un mecanismo de notificación implementado correctamente, si está esperando un evento . Si realmente necesita esperar una cantidad específica de tiempo para algo, entonces dormir es apropiado.

Por ejemplo, en algunos protocolos de red, utiliza una técnica conocida como retroceso exponencial, donde si detecta una colisión en la red, desea esperar una cantidad de tiempo aleatoria (y exponencialmente creciente) antes de volver a intentarlo. En este caso, dormir es apropiado porque no estás tratando de esperar que ocurra un evento, estás tratando de esperar que transcurra un tiempo específico.

He encontrado un buen uso para usar Thread.Sleep(1000); para simular la latencia al escribir una callback en una aplicación web.

Un ejemplo simple con los componentes AJAX de Microsoft es poner un botón en un panel de actualización … en el controlador onclick usar Thread.Sleep (), el panel de progreso de la actualización se mostrará durante ese período de tiempo.

Elimina el Thread.Sleep () cuando salgas a la venta … 🙂

En el mundo real, no siempre es práctico usar las primitivas de sincronización adecuadas:

  • Algunas veces necesita sondear algo hasta que se cumpla una condición, y allí usa sleep para evitar empapar la máquina.

  • A veces desea dormir activamente, por ejemplo, cuando quiere que un hilo tome recursos mínimos de la máquina y está en un sistema operativo ( tos de Windows) que no hace un buen trabajo al evitar que los subprocesos de baja prioridad se empapen la máquina.

Edita en respuesta al comentario de Kieveli: creo que deberías implementar la señalización adecuada cuando puedas. Entonces, un hilo de trabajo debería hacer una lectura de locking en una cola en lugar de dormir y sondear. Estoy diciendo que a veces te ves obligado a interactuar con sistemas que impiden hacer lo correcto. No digo que no debas molestarte en hacer lo correcto cuando sea razonablemente posible, “dentro de medio segundo es suficiente”, tal vez, pero casi instantáneo sería notablemente mejor.

La misma respuesta que para todas las otras preguntas “¿es malo?”

Es una operación de bajo nivel. La gente a veces usa operaciones de bajo nivel para reinventar ruedas que ya están disponibles como operaciones de alto nivel. Esto no es prudente, y debes asegurarte de que no sea lo que estás haciendo. Pero si en realidad está progtwigndo en un nivel bajo, entonces probablemente usará operaciones de bajo nivel. Algunos progtwigdores de alto nivel pensarán que eres malvado o estúpido o ambos. Usted sabe mejor, y ya que usted es quien escribe sus plataformas de alto nivel, pueden callarse o hacerlo por sí mismos.

Dada la elección entre una contorsión perversa usando una construcción de alto nivel, y una contorsión malvada usando una construcción de bajo nivel, algunas personas piensan que siempre deberías preferir la primera. Realmente no obtengo su punto de vista, pero al menos son consistentes, por lo que no puedes culparlos a menos que este último sea demostrablemente más rápido / más pequeño / mejor.

Buenos usos para dormir:

  • Cuando tiene que bloquear hasta cierto tiempo (o durante un cierto período). Su idioma / bibliotecas / sistema operativo puede o no ofrecer un temporizador de locking que hace el trabajo. Si no, entonces dormir es la opción siempre disponible. Si es así, probablemente hará exactamente lo mismo que el bucle de sueño estándar de pantano de todos modos, así que vaya con lo que sea menos código.

  • Al implementar otras primitivas, como temporizadores.

  • Cuando usa señales para mensajería asíncrona entre hilos o entre procesos en POSIX o lo que sea. Simplemente duerme todo el tiempo y los manejadores de señales hacen el trabajo o establecen el estado para decirle qué trabajo necesita hacer y lo hace cuando se despierta. Por supuesto, podrías usar otras API en su lugar, pero a menos que activamente prefieras esa API, entonces estarías evitando dormir porque algún tipo en internet te lo dijo. Tal vez cuando escribe un lenguaje de progtwigción que no tiene sueño, y es mejor que su idioma actual, entonces cambiará a eso.

El problema principal con el sueño (aparte de que no hace mucha interacción entre los hilos, por lo general es la herramienta incorrecta para los trabajos que lo requieren) es que aunque no consums tiempo de CPU, sigues consumiendo recursos. porque tienes un hilo sentado sin hacer nada. Dependiendo de la plataforma, los hilos pueden ser costosos. En un sistema de un solo núcleo, el segundo subproceso de dos es especialmente costoso, ya que si puede deshacerse de él la mayoría de sus problemas de sincronización de datos desaparecen. Por lo tanto, si puede diseñar razonablemente su aplicación de manera que nunca necesite varios hilos, y nunca bloques, excepto en algún bucle de eventos, porque todo se hace con mensajes asíncronos, hágalo y no necesitará dormir.

La implementación de tail -f requeriría un sleep() . es decir, stat() el archivo, si ha cambiado, lea en las diferencias, luego sleep() durante un bit …… continúe hasta que se interrumpa.

Consideraría esto como un uso válido del sleep()

Como ejemplo en Linux, strace tail -f foo , se establece en

 clock_gettime(CLOCK_REALTIME, {1247041913, 292329000}) = 0 nanosleep({1, 0}, NULL) = 0 fstat64(3, {st_mode=S_IFREG|0755, st_size=357, ...}) = 0 clock_gettime(CLOCK_REALTIME, {1247041914, 311933000}) = 0 nanosleep({1, 0}, NULL) = 0 fstat64(3, {st_mode=S_IFREG|0755, st_size=357, ...}) = 0 clock_gettime(CLOCK_REALTIME, {1247041915, 355364000}) = 0 .... 

OK esto es nanosleep() pero se aplican las mismas reglas.

La truss tail -f foo Solaris truss tail -f foo se establece en

 read(0, 0x00024718, 65537) = 0 nanosleep(0xFFBFF1A0, 0xFFBFF198) = 0 read(0, 0x00024718, 65537) = 0 nanosleep(0xFFBFF1A0, 0xFFBFF198) (sleeping...) nanosleep(0xFFBFF1A0, 0xFFBFF198) = 0 read(0, 0x00024718, 65537) = 0 

Además de los propósitos de prueba como el mencionado por Chalkey , descubrí que nunca necesito llamar a dormir en sistemas operativos de alto nivel como Linux, Windows y OS / X. Incluso en los casos en que se espera que el progtwig simplemente espere una cantidad de tiempo determinada, uso una función de espera en un semáforo con un tiempo de espera, para que pueda terminar el hilo de inmediato liberando el semáforo si algo / alguien le pide a mi progtwig que termine :

 # [pseudo-code] if wait_semaphore(exit_signal_semaphore, time_to_sleep) # another thread has requested this one to terminate end_this_thread else # event timed-out; wait_semaphore behaved like a sleep function do_some_task end 

No puedo verlo mencionado aquí, pero sleep () es una manera bastante segura de ceder el tiempo de tu CPU a un proceso más digno. Cuando duerme (), el sistema operativo toma el control y decide quién debe ejecutar el siguiente, esto también permite que otros subprocesos del mismo proceso tengan tiempo de CPU. Esto alivia la presión de la CPU en la máquina de una sola CPU cuando se generan varios hilos y un Controlador espera a que los Trabajadores estén listos.

Si está utilizando Sleep () para trabajar con cualquier caso que involucre algo más que el hilo en el que está operando, es probable que esté haciendo algo mal …

Dicho esto, hay momentos en los que desea pausar el procesamiento, por ejemplo, en un bucle de trabajo interminable (o de hecho), para dar a los demás espacio para respirar.

En mi humilde opinión, solo puede evitar un sueño () cuando tiene un sistema operativo bien escrito y está trabajando en el ámbito normal del sistema operativo.

Cuando tiene que trabajar fuera de un SO (por ejemplo, software / firmware incorporado) y fuera de los dominios normales (como los controladores), entonces no tiene acceso a las técnicas de subprocesamiento porque no hay nada que cambiar de tarea para usted. Como tal, no tienes más remedio que hacer cosas como spinlocks y esperas sondeadas ya que no hay preámbulo para venir a ayudarte.

Además, si se está integrando en una API no basada en eventos, a veces no tiene otra opción que sondear la información, de modo que en ese caso su mejor opción es dormir y no hacer giros para que el SO haga algo mientras espera.

El sueño se usa mal si se usa para “espera ocupada”. Sin embargo, en algunos casos, el modo de suspensión se usa en lockings giratorios o cuando no tiene una API que le permite trabajar con diferentes fonts de eventos.

La llamada sleep() casi siempre se puede evitar. A continuación se presentan algunos escenarios en un progtwig con un bucle de evento y múltiples hilos, ya que este sería el escenario más común en el que se usa (ab) sleep() .

  1. Como una función de monitoreo / sondeo (es decir, ‘espera-cada-100-ms-antes de repetir esta función’) – un mecanismo de tiempo de espera puede reemplazar la suspensión. El tiempo de espera esencialmente solicita que el controlador de eventos principal de la aplicación llame a una función después de un cierto tiempo. Este ‘recordatorio’ se almacena en una cola interna, y se ejecuta después de que el temporizador expira. p.ej:

     class DownloadMonitor { int UpdateProgressBar(); void Monitor() { if (UpdateProgressBar() < 100) { Application::SetTimeOut("100 ms", this, UpdateProgressBar); } } } 

    Esto aplica para ejemplos como "tail -f" también. Un tiempo de espera bien implementado (por ejemplo, verificar el tiempo de modificación del archivo) permitirá al usuario tener control sobre el progtwig entre las encuestas. Una desventaja de un tiempo de espera es que está restringido a hilos con bucles de eventos (este suele ser el hilo principal).

  2. Como mecanismo de sincronización de subprocesos, esto es altamente peligroso, ya que calcula el tiempo que tarda una CPU en procesar algo. Reemplace con semáforos binarios.

Un ejemplo simple de semáforos y tiempos de espera:

  • Un software de conversión de video típicamente tiene hilos separados para análisis de imágenes, disco IO y la IU (permitiendo comandos como "Ejecutar en segundo plano", "Cancelar", "Pausa", etc.).

  • El análisis de imágenes debe completarse antes de que se solicite al disco IO que escriba la parte convertida del archivo en el disco, y debe completarse antes de que la interfaz de usuario se actualice a sí misma.

  • Permita que ambos hilos se ejecuten en un bucle. El subproceso DiskIO comienza esperando en un semáforo de "Conversión de finalización". El análisis de imagen puede publicar este semáforo cuando una imagen convertida está en la cola. El subproceso Disk IO escribe esta imagen en el disco, actualiza una variable como "Porcentaje completado" y espera nuevamente en el semáforo. El subproceso de interfaz de usuario lee periódicamente la variable "Porcentaje completado" utilizando un mecanismo de tiempo de espera. Los diversos estados de UI los establece la interfaz de usuario y se comprueban al menos una vez en cada bucle mediante los hilos de conversión y de disco IO.

Probablemente, el único uso válido del sueño es simular actividad. Esto se utiliza mejor para las pruebas y se excluye de las comstackciones de los clientes.

A veces, no desea actualizar su pantalla continuamente, por ejemplo, en monitores de sistema o de red. Otro ejemplo es watch (1): ¿cómo implementaría eso sin dormir?

En Java y .Net, sleep () es interrumpible.

Por lo tanto, si duerme () y otro hilo lo interrumpe cuando lo desee (lo que hace que se eche una excepción InterruptedException), no hay nada de malo en eso.

Actualmente estoy escribiendo un mecanismo de votación sobre una cola externa, y mi código es básicamente:

 while (true) { items = takeFromQueue(); if (items.size() == 0) sleep(1000); } 

Y otro hilo puede detener este hilo de forma segura llamando a thread.interrupt ();

Windows: normalmente, si necesito hacer “sleep a thread” no usaría sleep (), en cambio usaría MsgWaitForMultipleObjects o similar, lo que permite especificar un tiempo de espera mientras se esperan las notificaciones. De esta forma, el hilo aún puede reactjsr ante cualquier evento (por ejemplo, WM_QUIT) sin que el proceso tenga que esperar a que el hilo salga del modo inactivo, verifique el evento y luego salga.

Hice un dispositivo para leer chips Flash, que controlo a través del puerto paralelo de mi computadora. Necesitaba esperar a que los datos se propagaran a través de los chips antes de enviar otra señal de reloj. Así que usé el usleep() .

Mi uso más común de Sleep es ilustrar problemas en código multiproceso, pero aparte de eso hay usos válidos como se menciona en algunas de las otras respuestas.

Se trata de cómo lo usas.

En algunos códigos que usamos en el trabajo, un desarrollador puso un sueño (5) 5 milisegundos, con el comentario de ‘dormir para que mi PC todavía pueda usarse’, lo cual está bien, excepto que nuestros clientes se quejaron del rendimiento de velocidad. Así que lo eliminé, y el rendimiento se duplicó, pero lo volvió a poner como sueño (0) quejándose de que su máquina no respondía cuando realizaba grandes pruebas de carga de datos. Groan , consigue una nueva PC de desarrollo

Donde, como no tengo ningún problema, realmente no desea cargar el sistema para dormir, ni usar el modo de suspensión junto con una acción de tipo de sondeo.

Pero, ¿y otros usos? ¿El sueño siempre es malo? Si no, ¿cuáles son los buenos casos de uso para dormir?

¿Qué tal si necesita hacer algo cada 10 minutos, no veo nada malo con solo poner su progtwig / hilo a dormir hasta que sea hora de volver a trabajar?