Pierwsza jest znacznie lepszą opcją.
Parallel.ForEach, wewnętrznie, używa Partitioner<T>
do dystrybucji twojej kolekcji na elementy pracy. Nie wykona jednego zadania na przedmiot, a raczej zgrupuje go, aby obniżyć związane z tym koszty ogólne.
Druga opcja zaplanuje jeden Task
na element w Twojej kolekcji. Chociaż wyniki będą (prawie) takie same, spowoduje to znacznie więcej kosztów ogólnych, niż to konieczne, szczególnie w przypadku dużych kolekcji, i spowoduje, że ogólne czasy działania będą wolniejsze.
FYI - używanym Partycjonerem można sterować za pomocą odpowiednich przeciążeń dla Parallel.ForEach , jeśli jest to pożądane. Aby uzyskać szczegółowe informacje, zobacz Niestandardowe partycjonery w MSDN.
Główną różnicą w czasie wykonywania jest to, że druga będzie działać asynchronicznie. Można to powielić za pomocą Parallel.ForEach, wykonując:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
W ten sposób nadal korzystasz z partycjonerów, ale nie blokuj, dopóki operacja nie zostanie zakończona.
Zrobiłem mały eksperyment z użyciem metody „1 000 000 000 (jeden miliard)” razy z „Parallel.For” i jednym z obiektami „Task”.
Zmierzyłem czas procesora i stwierdziłem, że Parallel jest bardziej wydajny. Równolegle. Dzieli twoje zadanie na małe elementy robocze i wykonuje je równolegle na wszystkich rdzeniach w optymalny sposób. Podczas tworzenia wielu obiektów zadań (FYI TPL wewnętrznie będzie korzystała z pulowania wątków) będzie przenosić każde wykonanie każdego zadania, powodując więcej stresu w polu, co jest widoczne z poniższego eksperymentu.
Stworzyłem również małe wideo, które wyjaśnia podstawową licencję TPL, a także pokazuje, jak Parallel.For bardziej efektywnie wykorzystuje Twój rdzeń http://www.youtube.com/watch?v=No7QqSc5cl8 w porównaniu do normalnych zadań i wątków.
Eksperyment 1
Eksperyment 2
źródło
Mehthod1()
w tym przykładzie?Parallel.ForEach zoptymalizuje (może nawet nie rozpocząć nowych wątków) i zablokuje, dopóki pętla nie zostanie zakończona, a Task.Factory wyraźnie utworzy nową instancję zadania dla każdego elementu i zwróci przed zakończeniem (zadania asynchroniczne). Parallel.Foreach jest znacznie wydajniejszy.
źródło
Moim zdaniem najbardziej realistycznym scenariuszem jest sytuacja, w której zadania wymagają dużej operacji. Podejście Shivprasad koncentruje się bardziej na tworzeniu obiektów / alokacji pamięci niż na samym przetwarzaniu. Przeprowadziłem badanie, które wywołało następującą metodę:
Wykonanie tej metody zajmuje około 0,5 sekundy.
Nazwałem to 200 razy przy użyciu Parallel:
Potem nazwałem to 200 razy staromodnym sposobem:
Pierwszy przypadek ukończono w 26656 ms, drugi w 24478 ms. Powtórzyłem to wiele razy. Za każdym razem drugie podejście jest marginalnie szybsze.
źródło