2010-07-23 5 views
5

Soy nuevo en C#, Parallel.ForEach y .NET en general. Quiero paralelizar una búsqueda que involucra miles de ubicaciones. Para cada ubicación, calculo la distancia del círculo grande. Ese es un cálculo que quiero extender a diferentes núcleos. Mi pregunta es ¿cómo puedo hacer esto si solo tengo una variable local del hilo, como en este MSDN TPL example? Para el resultado, miré Interlocked, y vi sus opciones Add, CompareExchange, Decrement, Exchange, Increment y Read, pero no solo estoy agregando, incrementando, disminuyendo o probando la igualdad. Quiero devolver el objeto, en varios subprocesos que se ejecutan en paralelo, que tiene la distancia total más corta distancia. Mi intuición dice que esto debería ser fácil, que debería ser capaz de crear un objeto pequeño que envuelve un Location y una distancia, pero ¿cómo puedo capturar la mejor respuesta de cada hilo y y luego elegir la distancia más corta entre ellos? Aquí está la versión no paralela:Elegir el mínimo entre los mínimos usando Parallel.ForEach

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    foreach (Location aLoc in allLocations) 
    { 
    if (aLoc != myLocation) 
    { 
     double d = greatCircle(myLocation, aLoc); 
     if (d < closest) 
     { 
     closest = d; 
     closestLoc = aLoc; 
     } 
    } 
    } 
    return closestLoc; 
} 

vi una DDJ Blog Post que parecía ofrecer un buen consejo, pero me preguntaba si era el mejor consejo. Veo al autor recorriendo las matrices y me pregunto si no hay una forma más funcional de hacerlo. En el mundo funcional usaría map, lambda y min.

Respuesta

10

La opción más fácil sería aquí para cambiar a PLINQ:

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    return allLocations 
       .AsParallel() 
       .Min(location => greatCircle(myLocation, location)); 
} 

Dicho esto, esto es, básicamente, sólo aggregation with parallel constructs. Tienes un par de opciones si quieres quedarte con la clase Parallel. Una opción sería sincronizarlo usted mismo dentro del bloque, usando el bloqueo. No recomendaría esto, ya que dañará tu rendimiento general.

La mejor opción es utilizar los métodos Parallel.ForEach que proporcionan el estado local. Que permitirían volver a escribir esto como:

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    object sync = new object(); 

    Parallel.ForEach<Location, Tuple<double,Location>(
     allLocations, 
    () => new Tuple(double.MaxValue, null), 
     (location, loopState, localState) => 
     { 
      double d = greatCircle(myLocation, aLoc); 
      if (d < localState.Item1) 
       return new Tuple(d, aLoc); 
      else 
       return localState; 
     }, 
     localState => 
     { 
      lock(sync) 
      { 
       if (localState.Item1 < closest) 
       { 
        closest = localState.Item1; 
        closestLoc = localState.Item2; 
       } 
      } 
     } 
); 
    return closestLoc; 
} 

cubro usando local state for aggregations in detail on my blog. Esto básicamente cambia la operación a una operación de bloqueo por hilo en lugar de un bloqueo por elemento de procesamiento, por lo que obtiene un rendimiento mucho mayor que una solución de bloqueo ingenua.

+0

Guau, eso es tan corto y dulce como se pone. Hace que mi corazón funcional cante. ¡Gracias! – gknauth

+0

@gknauth: Sí, también agregué la opción (correcto/rendimiento) Parallel.ForEach. Recomiendo aprender ** cómo ** funciona la otra opción y tratar de entenderlo. Sin embargo, es valioso para la mayoría de estas operaciones, solo uso PLINQ ya que es increíble. –

+0

Gracias por darme una pista en tu blog también, se ve muy bien. ¡Escribe un libro, lo compraré! O si eso es demasiado trabajo, te compraré la cena. – gknauth

Cuestiones relacionadas