thread c# multithreading wait thread-sleep

c# - Alternativas a usar Thread.Sleep para esperar



thread wait c# (3)

En primer lugar, no estoy haciendo la misma pregunta que C #: ¿Alternativa a Thread.Sleep? , o Alternativa a Thread.Sleep en C #? . No creo que lo esté usando incorrectamente y necesito una alternativa genuina para situaciones específicas.

Durante una ejecución de análisis de código vi una violación sorprendente que se avecinaba:

El uso de Thread.Sleep () es un signo de diseño defectuoso.

Esta violación lleva al artículo de Peter Richie sobre por qué esto constituye un mal diseño.

Todos sabemos que la creación de subprocesos es costosa y el bloqueo de subprocesos significa contención en el grupo. También sabemos que cada subproceso asignará un megas de memoria, por lo que debería tener una vida útil corta, el bloqueo en la interfaz de usuario es malo, el uso del reposo no es confiable, etc. Lo que me lleva a mi punto, si realmente necesita realizar un sueño, ¿qué deberías usar si no Thread.Sleep?

Peter continúa mencionando que el sueño cero es el único uso correcto de Thread.Sleep, que abandona efectivamente el segmento de tiempo del hilo y permite que otros hilos se procesen. Y aún más aterrador es que esto es solo debido a las limitaciones de los subprocesos no administrados y si se vuelve a implementar en el CLR creará efectos secundarios del uso de Thread.Sleep en sus aplicaciones. Todos los puntos sobre mal uso común son, de hecho, buenos ejemplos de mal uso.

Tengo las siguientes situaciones en el código de producción que utiliza Thread.Sleep con bastante éxito:

  • A la espera de que el sistema operativo renuncie a un bloqueo de archivos (detectar problemas de bloqueo de archivos, esperar un segundo , volver a intentarlo, abandonar después de un tiempo).
  • Matar un proceso y esperar a que no se muestre en la lista de procesos (elimínelo, verifique que no se esté ejecutando, espere un segundo , verifique que aún no se esté ejecutando, forzarlo a cerrarse).
  • Esperando a que se vacíen los buffers de copia (verifique el tamaño de un archivo, intente acceder a él, espere , verifique si el tamaño ha cambiado).

Sin usar Thread.Sleep en situaciones como estas, ¿qué otras opciones tengo? Los bucles cerrados tienden a empeorar las cosas y no creo que esto haga que su uso sea un "defecto de diseño", especialmente porque no hay nada en la interfaz de usuario y solo en hilos de fondo. Es justo la naturaleza del software esperar otras cosas en un entorno de múltiples hilos con factores externos que afectan su código, a veces necesita esperar ...


Bueno, dijiste la mayor parte. Cita "Todos sabemos que la creación de subprocesos es costosa y el bloqueo de subprocesos significa contención en el grupo", por lo que incluso comprende de qué se trata el uso de un grupo de subprocesos.

También entiendes que bloquear el hilo de UI es malo.

Mirando de nuevo el modelo de grupo de hilos: tienes un grupo de hilos, tal vez uno por procesador, y luego les pasas tareas. ¿Qué sentido tendría que bloquear uno de esos hilos? Si no tiene trabajo que realizar ahora, entonces simplemente debe proceder a una tarea diferente.

Entonces, diríjase directamente a su pregunta "Lo que me lleva a mi punto, si realmente necesita dormir, ¿qué debería usar si no Thread.Sleep?", En un programa moderno y bien diseñado, nunca tendría que hacer Simplemente programaría una tarea para este último.

Debe pensar en los subprocesos de un grupo, al igual que los procesadores en un sistema, como recursos, que deben ser liberados a otros cuando no son necesarios.

Siguiendo sus ejemplos, está demasiado involucrado en el paradigma de la programación imperativa.

  • No necesita esperar a que desaparezca el proceso ... No imagino por qué necesita esto, pero si tiene que esperar, es porque tiene trabajo que realizar después de algún tiempo, la "continuación" de su función. Debe configurar un temporizador para esta "continuación".
  • Los ejemplos de archivos deberían tener otros mecanismos para ello, si no tienen ... eso sería un buen diseño del sistema operativo. Por ejemplo, la única forma segura de esperar a que se vacíen los búferes sería una primitiva del sistema operativo, como la de fsync.
  • Si hay alguien que escribe en un archivo y luego otra lectura del archivo, entonces se necesita un mecanismo de sincronización, no una espera cronometrada (a menos que el archivo sea solo para agregar, en cuyo caso el archivo en sí es el mecanismo de sincronización).

Esperar en un mecanismo de sincronización no es "malo".


En uno de mis proyectos utilicé 2 subprocesos y tuve un problema con la congelación de la interfaz de usuario gracias a Thread.Sleep ... esto solucionó mi problema:

public static void Sleeping(int miliseconds) { var task = Sleep(miliseconds); task.Wait(); } public static async Task Sleep(int miliseconds) { await Task.Delay(miliseconds); }


El tipo WaitHandle y los tipos derivados proporcionan un mecanismo controlado por eventos para esperar que se vincula con el sistema operativo. Por ejemplo, cuando tiene una Task<T> task y espera el resultado accediendo a la task.Result , la implementación interna no está task.Result con las llamadas Thread.Sleep entre ellas. Está utilizando un tipo derivado de WaitHandle para hacer la espera y la sincronización.

A veces es necesario un enfoque basado en sondeo, como en algunos de los ejemplos que dio en su lista de viñetas, pero a menudo puede usar un enfoque basado en eventos. No es que Thread.Sleep sea siempre malo, es solo que a menudo se usa mal .

Es justo la naturaleza del software esperar otras cosas en un entorno de múltiples hilos con factores externos que afectan su código, a veces necesita esperar ...

Esperar está bien. Esperar con el sondeo a menudo no es (*). Si existe alguna forma de que pueda usar una espera controlada por un evento , normalmente debe esforzarse por usarla.

No tengo una idea muy clara de qué es exactamente lo que estás preguntando, así que no voy a explicar más allá de esto. Si dejas un comentario puedo ampliar mi respuesta.

(*) La razón teórica de esperar con el sondeo es mala es la siguiente:

Supongamos que tengo un código que se parece a esto:

//START Begin(); while (!Done()) Thread.Sleep(D); //STOP

Begin() inicia alguna operación. Done() devolviendo true significa que la operación ha finalizado. Supongamos que esto suceda después de aproximadamente T tiempo. Entonces:

  • El hilo se activa y verifica la condición (llama Done() ) T/D veces
  • La duración desde START hasta STOP incluye un D/2 esperado solo por Thread.Sleep

¿Qué valor de D debería elegir? A medida que aumenta D , la duración esperada de START to STOP aumenta linealmente. A medida que disminuye D , el número de iteraciones (limitado por) aumenta a 1/D Ambos son malos, y encontrar la D correcta es problemático.

Ahora compare esto con una espera basada en eventos :

//START Begin(); WaitDone(); //STOP

Teóricamente hablando, mientras WaitDone() alguna manera espere mágicamente hasta que la operación haya finalizado, pero ya no, los dos problemas identificados en el caso de espera con sondeo han desaparecido: este hilo espera exactamente la cantidad de tiempo correcta, no más, no ¡Menos!

Para reiterar el punto con el que comencé: en .NET, la clase WaitHandle y los tipos derivados son los que facilitan este enfoque.