Pregunta

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 máximo.Ese es un cálculo que quiero difundir a diferentes núcleos.Mi pregunta es como hago esto si solo tengo uno variable local de subproceso, como en este Ejemplo de TPL de MSDN?Para el resultado, miré Interlocked, y vio sus opciones Add, CompareExchange, Decrement, Exchange, Increment y Read, pero no solo estoy sumando, incrementando, disminuyendo o probando la igualdad.Quiero devolver el objeto, a través de varios subprocesos que se ejecutan en paralelo, que tiene el más corto en general distancia.Mi instinto me dice que esto debería ser fácil, que debería poder crear algún pequeño objeto que envuelva un Location y una distancia, pero ¿cómo puedo capturar la mejor respuesta de cada hilo y entonces elegir la distancia más corta entre ellos?Aquí está la versión no paralela:

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 un Publicación del blog de DDJ eso parecía ofrecer un buen consejo, pero me preguntaba si era el mejor consejo.Veo al autor recorriendo matrices y me pregunto si no hay una forma más funcional de hacerlo.En el mundo funcional usaría map, lambda y min.

¿Fue útil?

Solución

La opción más sencilla aquí sería 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 agregación con construcciones paralelas.Tienes un par de opciones si quieres seguir con la clase paralela.Una opción sería sincronizarlo usted mismo dentro del bloque, mediante el bloqueo.No recomendaría esto, ya que perjudicará su rendimiento general.

La mejor opción es utilizar el Paralelo.ParaCada métodos que prevén el estado local.Te permitirían reescribir 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 estado local para agregaciones en detalle en mi blog.Básicamente, esto cambia la operación a una operación de bloqueo por subproceso en lugar de un bloqueo por elemento de procesamiento, por lo que obtiene un rendimiento mucho mayor que una solución de bloqueo ingenua.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top