2012-07-09 14 views
7

Właśnie zacząłem używać licencji TPL i chcę wykonywać kilka połączeń z usługami sieciowymi równolegle. Z tego, co mogę zebrać, widzę dwa sposoby robienia tego.Jaki byłby lepszy sposób korzystania z biblioteki zadań równoległych

Albo 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 
       //... 
      }); 

Albo 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. 

Pomijając czy składnia jest poprawna, czy nie są one do equivilant?

Czy dadzą taki sam wynik? Jeśli nie, dlaczego? a który jest lepszy?

Odpowiedz

5

W przypadku omawianego kodu i sposobu użycia te dwa podejścia są w zasadzie równoważne.

Parallel.ForEach jest przydatny, gdy trzeba podzielić zakres wejściowy na kilka zadań (nie dotyczy tutaj), lub łatwiej jest zsynchronizować scalanie wyników kilku niezależnych operacji równoległych (być może w tym miejscu?).

W każdym razie prawidłowo zauważyłeś, że w przypadku Parallel.ForEach nie musisz ręcznie synchronizować oczekiwania na zakończenie, natomiast jeśli ręcznie uruchamiasz zadania, musisz samodzielnie zarządzać tą synchronizacją. W takim przypadku prawdopodobnie użyjesz czegoś takiego jak Task.WaitAll(...).

+0

Dziękuję za odpowiedź. Skończyło się na używaniu Parallel.ForEach i widzimy poprawę o około 25% w czasie reakcji na naszą produkcję - 16 podstawowych maszyn.Domyślam się, że większość korzyści wynika z równoległego przetwarzania danych, a nie z równoległych połączeń z usługami sieciowymi. –

1

Nie zastanawiając się ani nie patrząc na wynik, nie mogłem powiedzieć na pewno, czy oba są takie same; jednak wątpiłbym, czy są one tak różne. Pytanie, które jest lepsze, jest subiektywne w zależności od scenariusza. Aby odpowiedzieć, które jest preferowane, jest znowu bardzo subiektywne, w podanym przeze mnie scenariuszu powiedziałbym, że wolałbym Parallel.ForEach , ponieważ mogę go przeczytać, ale jeśli twój zespół programistów nie jest przyzwyczajony do biblioteki równoległej, to druga wersja to ten, do którego należy przejść.

1

Pomiędzy dwoma fragmentami kodu, Parallel.ForEach() będzie bardziej wydajny, ponieważ przetwarza wiele elementów w jednym Task, jeden po drugim.

Ale obaj użyją tylu wątków, ile zezwoli im ThreadPool, co nie jest dobrym pomysłem w tym przypadku. To dlatego, że ThreadPool jest dobry w odgadywaniu optymalnej liczby wątków, jeśli masz bardzo krótkie, związane z CPU Task s, co jest daleko od przypadku tutaj.

Z tego powodu uważam, że najlepszym rozwiązaniem jest ręcznie ograniczyć stopień równoległości do niewielkiej liczby (trzeba by zmierzyć się dowiedzieć, co liczba daje najlepsze wyniki):

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 
    //... 
}); 

Nawet bardziej wydajne byłoby, gdyby można było wykonać asynchroniczne wywołanie usługi WWW. Robienie tego i ograniczanie stopnia równoległości w tym samym czasie nie jest bardzo łatwe, chyba że jesteś w C# 5. Jeśli byłeś w C# 5 i jeśli również zaktualizowałeś Proxy w celu obsługi Wzorca Asynchronicznego (TAP) opartego na zadaniach, może wykorzystać TPL Dataflow do jeszcze skuteczniejszego wykonania kodu:

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