2009-01-29 8 views
18

Hoy tuve que arreglar algunos viejos códigos de VB.NET 1.0 que están usando hilos. 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.Cómo escribir código seguro/correcto de subprocesos múltiples en .NET?

Además del problema de la modificación concurrente mencionado anteriormente, existen puntos muertos, las condiciones de carrera, etc. uno puede encontrarse. Como depuración/fijación de los problemas de roscado es un dolor, me pregunto cómo podría reducir la codificación de errores/fallos en esta área y cómo podía más fácil encontrar a ninguno de ellos. Por lo tanto, lo que estoy pidiendo, es:

  • ¿Hay buenas pautas a seguir a la hora de escribir código multiproceso? ¿Cuáles son las cosas qué hacer y qué no hacer?
  • ¿Qué técnicas utiliza para depurar problemas de roscado?

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

Respuesta

24

Esto podría ser una lista masiva - lea la excelente "Concurrent Programming On Windows" de Joe Duffy para obtener más detalles. Esto es más o menos un volcado de cerebro ...

  • Trate de evitar poner en trozos significativos de código, mientras que es dueño de una cerradura
  • Evitar el bloqueo de referencias que el código externo a la clase también podría bloquear el
  • Si siempre 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 los hilos
  • Aparte de los tipos inmutables, intente evitar necesita compartir datos entre hilos
  • Evita tratar de hacer que tus tipos sean seguros para el hilo; la mayoría de los tipos no necesitan ser, y por lo general el código que necesita compartir datos tendrán que controlar el bloqueo en sí
  • En una aplicación de Windows Forms:
    • No realice ninguna operación de larga ejecución o de bloqueo en el hilo de interfaz de usuario
    • no toque la interfaz de usuario desde cualquier subproceso distinto del subproceso de interfaz de usuario. (Uso BackgroundWorker, Control.Invoke/BeginInvoke) las variables de subproceso local
  • evitar (también conocido como hilo-estática) cuando sea posible - que puede conducir a un comportamiento inesperado, sobre todo en ASP.NET que una petición puede ser atendida por diferentes hilos (búsqueda de "agilidad hilo" y ASP.NET)
  • no trate de ser inteligente. Código concurrente sin cerradura es enormemente difícil de obtener a la derecha.
  • Documente el modelo de subprocesos (y seguridad de subprocesos) de sus tipos
  • Monitor.Espere que casi siempre se use junto con algún tipo de comprobación, en un bucle while (es decir, mientras (no puedo continuar) Monitor. Espera (monitor))
  • considerar la diferencia entre Monitor.Pulse y Monitor.PulseAll cuidadosamente cada vez que utilice 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 cuanto a la 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!

+3

"pero no lo hacen olvida que el código entra en una especie de estado cuántico "- * suspiro * Conozco muy bien ese problema. –

11

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

Los 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 inherentemente 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.

mensaje de estilo Erlang pasar

No es necesario para aprender el idioma, pero echar un vistazo a Erlang para ver cómo se aproxima a la concurrencia. Las aplicaciones de Erlang pueden escalarse casi 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 inician y simplemente hacen girar un bucle esperando mensajes: los mensajes se reciben en forma de tuplas, con lo que el proceso puede coincidir para ver si el mensaje es significativo. Los procesos pueden enviar otros mensajes, pero son indiferentes a quien recibe el mensaje.

Ventajas de este estilo es la eliminación de bloqueos, cuando un proceso falla, no hace caer toda su aplicación. He aquí un buen resumen de la concurrencia de estilo Erlang: http://www.defmacro.org/ramblings/concurrency.html

+1

Buena respuesta. Es bueno ver que se centran en los enfoques de alto nivel del problema, en lugar de simplemente enumerar todas las primitivas de sincronización específicas de .NET. :) – jalf

1

Estos son los pasos para escribir la calidad (más fácil de leer y entender) Código de múltiples subprocesos:

  1. Mira la Power Threading Library por Jeffrey Richter
  2. reloj el video - asombrarse
  3. Tómese su tiempo para obtener una comprensión más profunda de lo que está sucediendo en realidad, lea los artículos de 'Concurrent Affair's found here.
  4. Comience a escribir aplicaciones multihilo robustas y seguras.
  5. damos cuenta de que todavía no es tan simple, cometer errores y aprender de ellos ... repetir ... repetir ... repetir :-)
2

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

1

Parece que nadie respondió la pregunta sobre cómo depurar programas multiproceso. Este es un verdadero desafío, porque si hay un error, necesita 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 trazado en sí debe:

  1. no añadir ningún retraso
  2. No utilice ningún bloqueo
  3. ser multihilo segura
  4. traza 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 multi-hilo de seguridad no bloqueante, y se puede llamar desde cualquier hilo. 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 de este enfoque, que también recoge el hilo y la información de tiempo, recicla el tampón y da salida a la traza bien se puede encontrar en: CodeProject: Depuración de código multiproceso en tiempo real 1

Cuestiones relacionadas