2012-07-09 11 views
7

? Acabo de empezar a usar el TPL, y quiero hacer varias llamadas a servicios web en paralelo. Por lo que puedo deducir, veo dos maneras de hacer esto.¿Cuál sería una mejor forma de usar la biblioteca paralela de tareas

De cualquier Parallel.ForEach:

List<ServiceMemberBase> list = new List<ServiceMemberBase>(); //Take list from somewhere. 
     Parallel.ForEach(list, member => 
      { 
       var result = Proxy.Invoke(member); 
       //... 
       //Do stuff with the result 
       //... 
      }); 

O Task<T>:

List<ServiceMemberBase> list = new List<ServiceMemberBase>(); //Take list from somewhere. 
     ForEach(var member in list) 
     { 
      Task<MemberResult>.Factory.StartNew(() => proxy.Invoke(member)); 
     } 

     //Wait for all tasks to finish. 
     //Process the result objects. 

sin tener en cuenta si la sintaxis es correcta o no, estas son las que brindaban?

¿Producirán el mismo resultado? Si no, ¿por qué? y cual es preferible?

Respuesta

5

Para el código y el caso de uso que analiza, los dos enfoques son esencialmente equivalentes.

Parallel.ForEach es útil cuando tiene que dividir un rango de entrada en varias tareas (no se aplica aquí), o es más fácil sincronizar la fusión de resultados de varias operaciones paralelas independientes (¿quizás aplicable aquí?).

En cualquier caso, ha notado correctamente que en el caso Parallel.ForEach, no tiene que sincronizar manualmente la espera para la finalización, mientras que si inicia tareas manualmente, tiene que administrar esa sincronización usted mismo. En este caso, probablemente usaría algo como Task.WaitAll(...).

+0

Gracias por la respuesta. Terminé usando Parallel.ForEach y vemos una mejora de alrededor del 25% en el tiempo de respuesta en nuestra producción: 16 máquinas centrales.Supongo que la mayor parte de la ganancia está en el procesamiento paralelo de datos, y no en las llamadas paralelas a los servicios web. –

1

Sin reflexionar ni mirar la salida, no podría decir con certeza si los dos son iguales o no; sin embargo, dudaría si son muy diferentes. La pregunta sobre cuál es mejor es subjetiva según el escenario. Responder lo que es preferible es una vez más muy subjetivo, en el escenario que proporcioné diría que preferiría el Parallel.ForEach porque puedo leerlo pero si su equipo de desarrollo no está acostumbrado a la biblioteca Parallel, entonces la segunda versión es el que ir.

1

Entre los dos códigos, Parallel.ForEach() será más eficiente, ya que procesa varios elementos en un solo Task, uno tras otro.

Pero ambos utilizarán tantos hilos como ThreadPool les permitan, lo cual no es una buena idea en este caso. Esto se debe a que el ThreadPool es bueno para adivinar el número óptimo de subprocesos si tiene Task s muy corto, vinculado a la CPU, que está lejos de ser el caso aquí.

Debido a esto, creo que la mejor opción es la de limitar manualmente el grado de paralelismo a un pequeño número (que tendría que medir para averiguar qué número le da los mejores resultados):

List<ServiceMemberBase> list = …; //Take list from somewhere. 
Parallel.ForEach(list, new ParallelOptions { MaxDegreeOfParallelism = 10 }, 
member => 
{ 
    var result = Proxy.Invoke(member); 
    //... 
    //Do stuff with the result 
    //... 
}); 

Incluso Sería más eficiente si pudiera ejecutar la llamada al servicio web de forma asíncrona. Hacer eso y limitar el grado de paralelismo al mismo tiempo no es muy fácil, a menos que esté en C# 5. Si estaba en C# 5 y si también actualizó Proxy para admitir el Patrón asincrónico basado en tareas (TAP), podría usar TPL Dataflow para ejecutar su código aún más eficientemente:

var actionBlock = new ActionBlock<ServiceMemberBase>(
    async member => 
    { 
     var result = await Proxy.InvokeAsync(member); 
     //... 
     //Do stuff with the result 
     //... 
    } 
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 }); 
Cuestiones relacionadas