thread net hilos c# .net vb.net multithreading

c# - hilos - ¿Cómo escribir código seguro/correcto multihilo en.NET?



hilos vb net (5)

No estoy seguro de lo bien que esto ayudará con la aplicación en particular con la que está trabajando, pero aquí hay dos enfoques tomados de la programación funcional para escribir código multiproceso:

Objetos inmutables

Si necesita compartir estado entre hilos, el estado debe ser inmutable. Si un hilo necesita hacer un cambio en el objeto, crea una nueva versión del objeto con el cambio en lugar de mutar el estado del objeto.

La inmutabilidad no limita intrínsecamente el tipo de código que puede escribir, ni es ineficiente. Hay muchas implementaciones de pilas inmutables, una variedad de árboles inmutables que forman la base de mapas y conjuntos, y otros tipos de estructuras de datos inmutables, y muchas (si no todas) las estructuras de datos inmutables son tan eficientes como sus contrapartes mutables.

Dado que los objetos son inmutables, no es posible que un hilo mute el estado compartido debajo de la nariz. Esto significa que no necesita adquirir bloqueos para escribir código multiproceso. Este enfoque elimina una clase completa de errores relacionados con el bloqueo de datos, bloqueo de secuencias y condiciones de carrera.

Transmisión de mensajes estilo Erlang

No necesita aprender el idioma, pero eche un vistazo a Erlang para ver cómo se acerca la concurrencia. Las aplicaciones de Erlang pueden escalarse prácticamente indefinidamente porque cada proceso está completamente separado de todos los demás (nota: estos no son exactamente procesos, pero tampoco son hilos exactamente).

Los procesos se disparan y simplemente hacen un ciclo esperando mensajes: los mensajes se reciben en forma de tuplas, con lo que el proceso puede coincidir con el patrón para ver si el mensaje es significativo. Los procesos pueden enviar otros mensajes, pero son indiferentes a quien recibe el mensaje.

Los beneficios de este estilo es la eliminación de bloqueos, cuando falla un proceso, no reduce toda la aplicación. Aquí hay un buen resumen de la concurrencia al estilo Erlang: http://www.defmacro.org/ramblings/concurrency.html

Hoy tuve que arreglar algunos viejos códigos de VB.NET 1.0 que están usando subprocesos. El problema era actualizar los elementos de la interfaz de usuario del hilo de trabajo en lugar del hilo de la interfaz de usuario. Me llevó algo de tiempo descubrir que puedo usar aserciones con InvokeRequired para encontrar el problema.

Además del problema de modificación simultánea mencionado anteriormente, hay puntos muertos, condiciones de carrera, etc. que uno podría encontrar. Como la depuración / reparación de problemas de enhebrado es un problema, me pregunto cómo podría reducir los errores / fallas de codificación en esta área y cómo podría encontrarlos más fácilmente. Entonces, lo que estoy pidiendo es:

  • ¿Hay algún buen patrón a seguir cuando se escribe código multihilo? ¿Cuáles son las cosas qué hacer y qué no hacer?
  • ¿Qué técnicas usas para solucionar problemas de subprocesos?

Proporcione un código de ejemplo si corresponde y posible. Las respuestas deben estar relacionadas con .NET Framework (cualquier versión).


Esta podría ser una lista masiva : lea la excelente " Programación concurrente en Windows " de Joe Duffy para obtener más detalles. Esto es más o menos un vertedero de cerebros ...

  • Intenta evitar llamar a trozos de código significativos mientras tienes un candado
  • Evite bloquear referencias que código fuera de la clase también puede bloquear
  • Si alguna vez necesita adquirir más de un candado a la vez, siempre adquiera esos candados en el mismo orden
  • Donde sea razonable, use tipos inmutables: se pueden compartir libremente entre hilos
  • Aparte de los tipos inmutables, trate de evitar la necesidad de compartir datos entre hilos
  • Evite tratar de hacer que sus tipos sean seguros para los hilos; la mayoría de los tipos no necesitan serlo, y generalmente el código que necesita compartir datos necesitará controlar el bloqueo en sí mismo
  • En una aplicación WinForms:
    • No realice ninguna operación de larga duración o bloqueo en el hilo de la interfaz de usuario
    • No toque la interfaz de usuario de ningún hilo que no sea el de la interfaz de usuario. (Utilice BackgroundWorker, Control.Invoke / BeginInvoke)
  • Evite las variables locales de subprocesos (aka thread-static) siempre que sea posible: pueden provocar un comportamiento inesperado, especialmente en ASP.NET donde una solicitud puede ser servida por diferentes subprocesos (busque "thread agility" y ASP.NET)
  • No intentes ser inteligente. El código simultáneo sin bloqueo es muy difícil de acertar.
  • Documente el modelo de subprocesamiento (y seguridad de subprocesos) de sus tipos
  • Monitor.Wait casi siempre debe utilizarse junto con algún tipo de control, en un ciclo while (es decir, mientras (no puedo continuar) Monitor.Wait (monitor))
  • Considera cuidadosamente la diferencia entre Monitor.Pulse y Monitor.PulseAll cada vez que utilizas uno de ellos.
  • Insertar Thread.Sleep para solucionar un problema nunca es una solución real.
  • Eche un vistazo a las "Extensiones paralelas" y el "Tiempo de ejecución de coordinación y concurrencia" como formas de simplificar la concurrencia. Parallel Extensions formará parte de .NET 4.0.

En términos de depuración, no tengo muchos consejos. Usar Thread.Sleep para aumentar las posibilidades de ver condiciones de carrera y bloqueos puede funcionar, pero debes tener una comprensión bastante razonable de lo que está mal antes de saber dónde colocarlo. El registro es muy útil, pero no olvide que el código entra en una especie de estado cuántico: ¡observarlo a través del registro casi inevitablemente cambiará su comportamiento!


Estos son los pasos para escribir código multihilo de calidad (más fácil de leer y comprender):

  1. Echa un vistazo a la biblioteca de Power Threading por Jeffrey Richter
  2. Mira el video - sorprendete
  3. Tómese su tiempo para obtener una comprensión más profunda de lo que está sucediendo realmente, lea los artículos de ''Afirmación concurrente'' que se encuentran aquí .
  4. ¡Comience a escribir aplicaciones robustas y seguras de múltiples hilos!
  5. Darse cuenta de que todavía no es tan simple, cometer algunos errores y aprender de ellos ... repetir ... repetir ... repetir :-)

Use FIFOs. Muchos de ellos. Es el secreto ancestral del programador de hardware, y se salvó mi tocino más de una vez.


Parece que nadie respondió la pregunta sobre cómo depurar programas multiproceso. Este es un verdadero desafío, porque si hay un error, debe ser investigado en tiempo real, lo cual es casi imposible con la mayoría de las herramientas como Visual Studio. La única solución práctica es escribir trazas, aunque el rastreo mismo debería:

  1. no agregar ningún retraso
  2. no use ningún bloqueo
  3. ser multihilo seguro
  4. rastrear lo que sucedió en la secuencia correcta.

Esto suena como una tarea imposible, pero se puede lograr fácilmente escribiendo la traza en la memoria. En C #, se vería algo como esto:

public const int MaxMessages = 0x100; string[] messages = new string[MaxMessages]; int messagesIndex = -1; public void Trace(string message) { int thisIndex = Interlocked.Increment(ref messagesIndex); messages[thisIndex] = message; }

El método Trace () es seguro para subprocesos múltiples, no bloquea y se puede invocar desde cualquier subproceso. En mi PC, lleva unos 2 microsegundos ejecutar, lo que debería ser lo suficientemente rápido.

Agregue instrucciones de Trace () donde crea que algo podría ir mal, deje que el programa se ejecute, espere hasta que ocurra el error, detenga el rastreo y luego investigue el rastreo para detectar cualquier error.

Una descripción más detallada para este enfoque que también recopila información sobre el hilo y el tiempo, recicla el búfer y muestra el rastro muy bien que puede encontrar en: CodeProject: Depuración de código multiproceso en tiempo real 1