2012-05-10 7 views
12

Los delegados son algunos de los objetos que facilitan el enhebrado en .NET reference. Se pueden usar para invocar asincrónicamente un método. ¿Qué otros objetos existen en el marco 4.5 (o anterior) que hacen que el uso de subprocesos sea más fácil o menos propenso a errores?¿Qué construcciones de alto nivel de .NET 4.5 (o anteriores) hacen que el enhebrado sea más fácil?

¿Cuáles son las otras abstracciones que facilitan la concurrencia y el subprocesamiento múltiple?

Nota: Esta pregunta actualiza this.

+4

Los delegados y eventos no tienen nada que ver con el subprocesamiento, y no son específicos de .NET 4.5. Sé más claro, ya que no puedo decir de qué estás hablando. –

+0

@JohnSaunders Los delegados Async facilitan el enhebrado; vea http://msdn.microsoft.com/en-us/library/22t547yb.aspx – LamonteCristo

+0

¿Ha notado que el uso asincrónico de delegados ha estado en .NET desde la versión 1.0? –

Respuesta

21

Tiendo a responder muchas preguntas relacionadas con el multihilo y a menudo veo la misma pregunta básica de diferentes maneras. Presentaré los problemas más comunes como los he visto a lo largo de los años y explicaré cómo las tecnologías más nuevas han hecho que la solución de estos problemas sea más fácil.

cerrándose sobre la variable de bucle

Esto no es un problema específico de rosca, pero el uso de enhebrar definitivamente magnifica el problema. C# 5.0 corrige este problema para el bucle foreach creando una nueva variable para cada iteración. Ya no tendrá que crear una variable especial para cierres de expresiones lambda. Desafortunadamente, el bucle for todavía necesitará ser manejado con una variable de captura especial.

la espera de tareas asíncronas para completar

.NET 4.0 introdujo la clase CountdownEvent que encapsula una gran cantidad de la lógica necesaria para esperar a la finalización de las muchas tareas. La mayoría de los desarrolladores junior usa Thread.Join llamadas o una sola llamada WaitHandle.WaitAll. Ambos tienen problemas de escalabilidad. El viejo patrón era usar un solo ManualResetEvent y señalarlo cuando un contador llegaba a cero. El contador se actualizó utilizando la clase Interlocked. CountdownEvent hace que este patrón sea mucho más fácil. Solo recuerde tratar a su principal como trabajador para evitar esa sutil condición de carrera que puede ocurrir si un trabajador termina antes de que todos los trabajadores hayan sido puestos en cola.

.NET 4.0 también introdujo la clase Task que puede tener tareas hijo encadenadas a través de TaskCreationOptions.AttachedToParent. Si llama al Task.Wait en un padre esperará a que se completen todas las tareas secundarias.

productor-consumidor

.NET 4.0 introdujo la clase BlockingCollection que actúa como una cola normal, excepto que puede bloquear cuando la colección está vacía. Puede poner en cola un objeto llamando al Add y quitar la cola de un objeto llamando al Take. Take bloques hasta que haya un artículo disponible. Esto simplifica considerablemente la lógica del productor-consumidor. Solía ​​ocurrir que los desarrolladores intentaban escribir su propia clase de cola de bloqueo. Pero, si no sabes lo que estás haciendo, entonces realmente puedes arruinarlo ... mal. De hecho, durante mucho tiempo, Microsoft tenía un ejemplo de cola de bloqueo en la documentación de MSDN que estaba mal roto. Afortunadamente, desde entonces se eliminó.

Actualización de la interfaz de usuario con el progreso subproceso de trabajo

La introducción de BackgroundWorker hizo la escisión de una tarea de fondo desde una aplicación WinForm mucho más fácil para los desarrolladores novatos. La principal ventaja es que puede llamar al ReportProgress desde el controlador de eventos DoWork y los controladores de eventos ProgressChanged se ordenarán automáticamente en el hilo de la interfaz de usuario. Por supuesto, cualquiera que rastree mis respuestas en SO sabe cómo me siento acerca de las operaciones de clasificación (a través del Invoke o similar) como una solución para actualizar la UI con información de progreso simple. Lo rasgo todo el tiempo porque generalmente es un enfoque terrible. BackgroundWorker todavía obliga al desarrollador a un modelo de inserción (mediante operaciones de clasificación en segundo plano), pero al menos hace todo esto detrás de escena.

La falta de elegancia de Invoke

Todos sabemos que un elemento de interfaz de usuario sólo se puede acceder desde el subproceso de interfaz de usuario. Esto generalmente significaba que un desarrollador tenía que usar las operaciones de cálculo de referencias a través de ISynchronizeInvoke, DispatcherObject o SynchronizationContext para transferir el control nuevamente al hilo de la interfaz de usuario. Pero admitámoslo. Estas operaciones de clasificación se ven feas. Task.ContinueWith hizo esto un poco más elegante, pero la verdadera gloria es await como parte del nuevo modelo de programación asincrónica de C# 5. await se puede utilizar para esperar a que Task se complete de tal manera que el control de flujo se interrumpa temporalmente mientras se ejecuta la tarea y luego se devuelve en ese mismo lugar en el contexto de sincronización correcto. No hay nada más elegante y satisfactorio que usar await como reemplazo de todas esas llamadas Invoke.

Programación paralela

A menudo veo preguntas preguntando cómo las cosas pueden ocurrir en paralelo. La forma antigua era crear algunos hilos o usar el ThreadPool. .NET 4.0 dio uso de TPL y PLINQ. La clase Parallel es una excelente forma de obtener las iteraciones de un ciclo en paralelo. Y el AsParallel de PLINQ es un lado diferente de la misma moneda para el LINQ antiguo. Estas nuevas características de TPL simplifican enormemente esta categoría de programación multiproceso.

.NET 4.5 presenta la biblioteca TPL Data Flow. Está destinado a hacer elegante un complejo problema de programación en paralelo. Resume clases en bloques. Pueden ser bloques de destino o bloques de origen. Los datos pueden fluir de un bloque a otro. Hay muchos bloques diferentes, incluidos BufferBlock<T>, BroadcastBlock<T>, ActionBlock<T>, etc. que hacen cosas diferentes. Y, por supuesto, toda la biblioteca estará optimizada para su uso con las nuevas palabras clave async y await. Es un nuevo conjunto de clases emocionantes que creo que lentamente se pondrán de moda.

finalización de precaución

¿Cómo se obtiene un hilo que pare? Veo esta pregunta mucho. La forma más fácil es llamar al Thread.Abort, pero todos sabemos los peligros de hacer esto ... espero. Hay muchas formas diferentes de hacerlo de forma segura. .NET 4.0 introdujo un concepto más unificado llamado cancelación a través de CancellationToken y CancellationTokenSource. Las tareas en segundo plano pueden sondear IsCancellationRequested o simplemente llamar al ThrowIfCancellationRequested en puntos seguros para interrumpir con gracia cualquier trabajo que estén haciendo. Otros hilos pueden llamar al Cancel para solicitar la cancelación.

+0

¡Guau, esto es justo lo que necesitaba! Estaba buscando una vista de 1,000 pies de cosas y esto es todo. La gente muy mala cerró esta pregunta; Lo nominé para reapertura. – LamonteCristo

+0

¿Las extensiones reactivas (Rx) y StreamInsight también deberían estar en esta lista? – LamonteCristo

+0

@ makerofthings7: Probablemente ... cuando tenga tiempo lo haré. –

1

El Task y Task<T>, pero han estado aquí desde .NET 4. async no funciona necesariamente con hilos, consulte Jon's video from Øredev por una muy buena explicación.

2

Sin lugar a dudas, familiarizarse con la nueva biblioteca Tpl DataFlow (incluida en .net 4.5) le dará el mayor impulso en términos de desarrollo simultáneo.

Si usted es serio sobre aplicaciones altamente concurrentes, pasar un día o dos a familiarizarse con flujo de datos. Es realmente bueno.

7

Bueno vamos a ver aquí:

  1. La clase ThreadPool - un poco viejo, pero todavía fiable para un simple patrón de productor-consumidor.
  2. BackgoundWorker (.NET 2.0+) - otra construcción de la vieja escuela, que proporciona características útiles para la ejecución de tareas en segundo plano en aplicaciones GUI.
  3. Timer s - útil para ejecutar el código en intervalos especificados utilizando un hilo de fondo.
  4. El Task clase (.NET 4.0 +) - enhebrar abstracciones que se ejecutan en el grupo de subprocesos subyacente y proporcionan muchas características útiles como el cálculo de referencias excepción y programación. Útil para el llamado patrón de "paralelismo de tareas".
  5. Parallel.For, Parallel.ForEach (.NET 4.0+) - buena para la ejecución de la misma operación sobre un conjunto de datos en paralelo. Útil para el llamado patrón de "paralelismo de datos".
  6. Parallel.Invoke (.NET 4.0 +) - una abstracción aún más en Task s. Simplemente dispara varias piezas de código (métodos, lambdas) en paralelo.
  7. Colecciones concurrentes (.NET 4.0+): todo lo que necesita para pasar o compartir datos entre hilos de una manera eficiente y segura para hilos.
Cuestiones relacionadas