Pregunta

Nos gustaría construir un patrón alrededor de ciertos bucles en nuestra solución, que les permitirá ejecutarse en serie o en paralelo, dependiendo de los factores.A continuación se muestra la forma general del mismo.

Dado que las colecciones concurrentes no comparten una interfaz común con las colecciones normales, necesitamos algún tipo de adaptador para escribir código general.

Específicamente en torno al uso de la addFunc delegado en el cuerpo del bucle, ¿hay algo ahí que terminaría causando problemas a largo plazo que podríamos pasar por alto?Funciona bien por ahora, pero....?

Action<SomeType> addFunc;

if(runInParallel)
{
   addFunc = concurrentBag.Add;
   loopDelegate = Parallel.ForEach;
}
else
{
   addFunc = iList.Add;
   loopDelegate = Serial.ForEach; // wrapper delegate for foreach
}

loopDelegate(source, item =>
{
   SomeType result = longRunningTask(item);
   ...
   addFunc(result); // will this 
});
¿Fue útil?

Solución

¿Tienes curiosidad por saber por qué no utilizar TPL en .NET 4.0?http://msdn.microsoft.com/en-us/library/dd537609.aspx

Hay un excelente documento técnico sobre las consideraciones que han tomado al desarrollar TPL. Si no puede usar .NET 4, debe mirar el documento y considerar algunos de los problemas que contiene.

Actualizado basado en un comentario que señala lo obvio.

Usaría un poco de azúcar sintáctico como,

ForEach<Tsource>(Predicate<IEnumerable<TSource>> isParallel, IEnumerable<TSource> source, Action<TSource> body)
{
    if(isParallel(source))
    {
        Parallel.ForEach<TSource>(source, body);
    }
    else
    {
        foreach (TSource element in source)
        {
            body(element);
        }
    }
}

Hay dos ventajas principales sobre su implementación.

  1. Se enumera dos veces, una para agregar los elementos que se van a recorrer y una segunda vez durante la ejecución.
  2. No es inmediatamente obvio, pero estás evitando que Parallel.ForEach y foreach utilicen el captador más eficiente.Parallel.ForeEach no siempre usará GetEnumerator en IEnumerable.Un enumerador por diseño no es concurrente; si su elemento implementa IList, Parallel.ForEach usará el indexador para permitir que cada subproceso acceda al elemento en la fuente sin esperar a que el enumerador repita la lista.ConcurrentBag no implementa IList.

Este es el documento al que me refería por cierto, http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=19222.

Todo es una muy buena lectura si estás mejorando esto, pero presta especial atención a las páginas 1 [5-7], 26, 3 [0-2].

El azúcar sintáctico significa que puedes llamarlo como lo harías con TPL,

MyParllelLibrary.ForEach( (list) => true, list), item =>
{
    // What my code does
});
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top