Jak przekonwertować ten kod foreach na Parallel.ForEach?

180

Jestem trochę zdezorientowany Parallel.ForEach.
Co to jest Parallel.ForEachi co dokładnie robi?
Proszę nie odwoływać się do żadnego łącza MSDN.

Oto prosty przykład:

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);

foreach (string line in list_lines)
{
    //My Stuff
}

Jak mogę przepisać ten przykład Parallel.ForEach?

SilverLight
źródło
Można na to odpowiedzieć tutaj stackoverflow.com/questions/3789998/…
Ujjwal Manandhar,
1
@UjjwalManandhar To właściwie całkiem inne, ponieważ pyta o różnicę między Parallelklasą a użyciem PLINQ.
Reed Copsey,
18
Inni odpowiedzieli, jak możesz przepisać. Co to robi? Wykonuje „akcję” na każdym elemencie w kolekcji, tak jak normalnie foreach. Różnica polega na tym, że wersja równoległa może wykonywać wiele „akcji” jednocześnie. W większości przypadków (w zależności od tego, na którym komputerze jest uruchomiony kod, jak jest zajęty i innych rzeczy) będzie on szybszy, a to jest najważniejsza zaleta. Pamiętaj, że gdy robisz to równolegle, nie możesz wiedzieć, w jakiej kolejności są przetwarzane elementy. W przypadku zwykłego (seryjnego) foreachmasz gwarancję, że lines[0]najpierw, a potem lines[1]itd.
Jeppe Stig Nielsen
1
@JeppeStigNielsen To będzie nie zawsze być szybciej, ponieważ istnieje znaczące napowietrznych z dokonywania rzeczy równolegle. Zależy to od wielkości kolekcji, na której się iterujesz, i działania w niej. Właściwe jest, aby faktycznie zmierzyć różnicę między użyciem Parallel.ForEach () a użyciem foreach (). Wiele razy normalne foreach () jest szybsze.
Dave Black
3
@DaveBlack Sure. W każdym przypadku trzeba będzie zmierzyć, czy jest to szybsze czy wolniejsze. Próbowałem tylko opisać paralelizację w ogóle.
Jeppe Stig Nielsen

Odpowiedzi:

126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});
FUNT
źródło
6
Chciałem tylko zwrócić na to uwagę (więcej dla OP), aby nie było mylnej myśli, że to działa tylko List<T>;)
Reed Copsey
1
dzięki za uwagę i odpowiedź. Użyłem List <ciąg> w moich kodach z powodu usuwania zduplikowanych elementów przy użyciu list HASH. przy zwykłej tablicy nie możemy łatwo usunąć duplikatów :).
SilverLight,
119
Jestem zdezorientowany, że ta odpowiedź jest oznaczona jako prawidłowa odpowiedź, ponieważ nie ma wyjaśnienia w oryginalnych postach pytanie „Co to jest Równoległe. Dla
każdego
6
@fosb Problem polega na tym, że tytuł pytania został zredagowany w celu całkowitej zmiany znaczenia ... więc ta odpowiedź nie ma już sensu. To powiedziawszy, to wciąż słaba odpowiedź
aw04
274

Foreach loop:

  • Iteracje następują kolejno, jeden po drugim
  • pętla foreach jest uruchamiana z jednego wątku.
  • Pętla foreach jest zdefiniowana w każdym systemie .NET
  • Wykonanie powolnych procesów może być wolniejsze , a oni uruchomić seryjnie
    • Proces 2 nie może rozpocząć się, dopóki nie zostanie ukończony 1. Proces 3 nie może się rozpocząć, dopóki nie zostaną wykonane 2 i 1 ...
  • Wykonywanie szybkich procesów może być szybsze , ponieważ nie ma narzutu związanego z gwintowaniem

Parallel.ForEach:

  • Wykonanie odbywa się równolegle.
  • Parallel.ForEach używa wielu wątków.
  • Parallel.ForEach jest zdefiniowany w frameworku .Net 4.0 i nowszych.
  • Wykonywanie wolnych procesów może być szybsze , ponieważ można je uruchamiać równolegle
    • Procesy 1, 2 i 3 mogą działać jednocześnie (patrz ponownie wykorzystane wątki w przykładzie poniżej)
  • Realizacja szybkich procesów może być wolniejsza z powodu dodatkowego narzutu na gwintowanie

Poniższy przykład wyraźnie pokazuje różnicę między tradycyjną pętlą foreach a

Przykład Parallel.ForEach ()

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelForEachExample
{
    class Program
    {
        static void Main()
        {
            string[] colors = {
                                  "1. Red",
                                  "2. Green",
                                  "3. Blue",
                                  "4. Yellow",
                                  "5. White",
                                  "6. Black",
                                  "7. Violet",
                                  "8. Brown",
                                  "9. Orange",
                                  "10. Pink"
                              };
            Console.WriteLine("Traditional foreach loop\n");
            //start the stopwatch for "for" loop
            var sw = Stopwatch.StartNew();
            foreach (string color in colors)
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            Console.WriteLine("foreach loop execution time = {0} seconds\n", sw.Elapsed.TotalSeconds);
            Console.WriteLine("Using Parallel.ForEach");
            //start the stopwatch for "Parallel.ForEach"
             sw = Stopwatch.StartNew();
            Parallel.ForEach(colors, color =>
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            );
            Console.WriteLine("Parallel.ForEach() execution time = {0} seconds", sw.Elapsed.TotalSeconds);
            Console.Read();
        }
    }
}

Wynik

Traditional foreach loop
1. Red, Thread Id= 10
2. Green, Thread Id= 10
3. Blue, Thread Id= 10
4. Yellow, Thread Id= 10
5. White, Thread Id= 10
6. Black, Thread Id= 10
7. Violet, Thread Id= 10
8. Brown, Thread Id= 10
9. Orange, Thread Id= 10
10. Pink, Thread Id= 10
foreach loop execution time = 0.1054376 seconds

Za pomocą przykładu Parallel.ForEach

1. Red, Thread Id= 10
3. Blue, Thread Id= 11
4. Yellow, Thread Id= 11
2. Green, Thread Id= 10
5. White, Thread Id= 12
7. Violet, Thread Id= 14
9. Orange, Thread Id= 13
6. Black, Thread Id= 11
8. Brown, Thread Id= 10
10. Pink, Thread Id= 12
Parallel.ForEach() execution time = 0.055976 seconds
Jignesh.Raj
źródło
63
Naprawdę nie zgadzam się z twoim „twierdzeniem”, że Parallel.ForEach jest (zawsze) szybszy. To naprawdę zależy od ciężkości operacji wewnątrz pętli. To może, ale nie musi być warte narzucenia wprowadzenia paralelizmu.
Martao
1
Cóż, równoległość dla każdego oznacza, że ​​są skonfigurowane osobne wątki do wykonania kodu w ciele pętli. Mimo, że .NET ma wydajny mechanizm do tego, jest to znaczny narzut. Jeśli więc wystarczy prosta operacja (np. Suma lub pomnożenie), równoległe foreach nie powinno być szybsze.
Martao
3
@Jignesh nie jest to nawet dobry przykład pomiaru, więc w ogóle o tym nie wspomnę. Usuń „Thread.Sleep (10);” z każdego elementu pętli i spróbuj ponownie.
stenly
1
@Martao ma rację, problemem są narzuty blokujące obiekty, w których podejście równoległe może być dłuższe niż sekwencyjne.
stenly
8
@stenly Myślę, że sen jest właśnie powodem, dla którego jest to dobry przykład. Nie użyłbyś PFE z szybkimi pojedynczymi iteracjami (jak wyjaśniła Martao) - więc ta odpowiedź powoduje, że iteracja jest powolna, a (poprawna) przewaga PFE jest podświetlona. Zgadzam się jednak, że należy to wyjaśnić w odpowiedzi, odważne „zawsze szybciej” jest bardzo mylące.
mafu
43
string[] lines = File.ReadAllLines(txtProxyListPath.Text);

// No need for the list
// List<string> list_lines = new List<string>(lines); 

Parallel.ForEach(lines, line =>
{
    //My Stuff
});

Spowoduje to, że linie będą analizowane równolegle w pętli. Jeśli chcesz bardziej szczegółowego, mniej „zorientowanego na referencje” wprowadzenia do klasy Parallel, napisałem serię na TPL, która zawiera sekcję o Parallel.ForEach .

Reed Copsey
źródło
9

W przypadku dużych plików użyj następującego kodu (jesteś mniej głodny pamięci)

Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line => {
    //Your stuff
});
Samuel LEMAITRE
źródło
2

Te linie działały dla mnie.

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
Książę Prasad
źródło