2012-04-14 22 views
39

¿La función .net Parallel.ForEach bloquea el hilo de llamada? Supongo que el comportamiento es uno de estos:¿Parallel.ForEach Block?

  1. Sí, bloquea hasta que vuelve el elemento más lento que se ejecuta.
  2. No, no bloquea y devuelve el control inmediatamente. Los elementos para ejecutar en paralelo se realizan en hilos de fondo.

O tal vez está sucediendo algo más, ¿alguien lo sabe con certeza?

Esta pregunta se acercó la hora de implementar esto en una clase de registro:

public class MultipleLoggingService : LoggingServiceBase 
{ 
    private readonly List<LoggingServiceBase> loggingServices; 

    public MultipleLoggingService(List<LoggingServiceBase> loggingServices) 
    { 
     this.loggingServices = loggingServices; 
     LogLevelChanged += OnLogLevelChanged; 
    } 

    private void OnLogLevelChanged(object sender, LogLevelChangedArgs args) 
    { 
     loggingServices.ForEach(l => l.LogLevel = LogLevel); 
    } 

    public override LogMessageResponse LogMessage(LogMessageRequest request) 
    { 
     if (request.LogMessage) 
      Parallel.ForEach(loggingServices, l => l.LogMessage(request)); 

     return new LogMessageResponse{MessageLogged = request.LogMessage}; 
    } 
} 

Aviso LogMessage el método llama a algunos otros servicios de registro. Necesito que esa parte vuelva de inmediato, por lo que no bloquea el hilo de llamada.


Actualización: En base a los comentarios de otras personas (que han confirmado el comportamiento es # 1). Así que he seguido el consejo de usar la biblioteca Task y reescrito el bucle de la siguiente manera:

  if (request.LogMessage) 
      foreach (var loggingService in loggingServices) 
       Task.Factory.StartNew(() => loggingService.LogMessage(request)); 

Respuesta

48

número 1 es correcta; Parallel.ForEach no regresa hasta que el ciclo se haya completado. Si no desea ese comportamiento, simplemente puede ejecutar su ciclo como Task y ejecutarlo en otro hilo.

+0

Gracias por la punta! ¿Tiene un pequeño fragmento de código sobre cómo usar 'Tarea' en este contexto? Gracias, Paul –

+1

@PaulFryer: 'Tarea t = Tarea.TaskFactory.StartNew (() => {/ * Paralelo.For va aquí * /});' – Richard

+6

Técnicamente, el hilo de llamada se utiliza en el bucle paralelo. Por lo tanto, no se trata de un "desperdicio" que solo bloquea el ciclo; se usa como uno de los hilos de trabajo. –

8

Re su actualización, StartNew en un foreach() normal:

esto puede no ser el más óptimo para grandes colecciones, y usted no consigue un punto para controlar los errores.

Su loggingServices probablemente no contiene miles de elementos, pero el manejo de errores sigue siendo un punto.

considerar:

Task.Factory.StartNew(() => 
{ 
    try 
    { 
     Parallel.ForEach(loggingServices, l => l.LogMessage(request)); 
    } 
    catch(SomeException ex) 
    { 
     // at least try to log it ... 
    } 
}); 
+0

Ah, buen punto. Tendré que pensar en esto por un momento, si el servicio de registro falla, cómo registrar el error :-) –