Як я можу перетворити цей код foreach в Parallel.ForEach?


180

Я трохи розгублений Parallel.ForEach.
Що таке Parallel.ForEachі що це робить саме?
Будь ласка, не посилайтеся на жодне посилання MSDN.

Ось простий приклад:

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

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

Як можна переписати цей приклад Parallel.ForEach?


Це , можливо, був дан відповідь тут stackoverflow.com/questions/3789998 / ...
Ujjwal Manandhar

1
@UjjwalManandhar Це насправді зовсім інакше, оскільки це запитує про різницю між Parallelкласом та використанням PLINQ.
Рід Копсей

18
Інші відповіли, як можна переписати. Так що це робить? Він робить "дію" на кожен предмет колекції, як і звичайний foreach. Різниця полягає в тому, що паралельна версія може робити багато "дій" одночасно. У більшості випадків (залежно від того, на якому комп’ютері працює код і наскільки він зайнятий та інші речі) він буде швидшим, і це найголовніша перевага. Зауважте, що ви робите це паралельно, ви не можете знати, в якому порядку обробляються елементи. З звичайним (серійним) foreach, ви гарантуєте, що lines[0]спочатку прийде, потім lines[1]тощо.
Jeppe Stig Nielsen

1
@JeppeStigNielsen Це не завжди буде швидше, оскільки є значні накладні витрати, щоб зробити речі паралельними. Це залежить від розміру колекції, яку ви повторюєте, та дії в ній. Правильна річ - це фактично виміряти різницю між використанням Parallel.ForEach () та використанням foreach (). У багато разів нормальний foreach () швидше.
Дейв Блек

3
@DaveBlack Звичайно. Треба буде вимірювати , швидше чи повільніше, у кожному конкретному випадку. Я просто намагався описати паралелізацію взагалі.
Джеппе Стіг Нільсен

Відповіді:


126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});

6
Просто хотілося вказати на це (більше для ОП), щоб не було помилкової думки, що це працює лише List<T>;)
Рід Копсей

1
дякую за увагу та відповідь. Я використовував List <string> у своїх кодах через видалення повторюваних елементів за допомогою списків HASH. за допомогою регулярного масиву ми не можемо легко видалити дублікати :).
SilverLight

119
Мене бентежить, що ця відповідь позначена як правильна відповідь, оскільки немає пояснення до питання про початкові повідомлення "Що таке паралельне. Для кожного і що це робить?" ...
fose

6
@fosb Проблема в тому, що назва питання була відредагована, щоб повністю змінити значення ... тому ця відповідь більше не має сенсу. Сказавши це, це все ще погана відповідь
aw04

274

Цикл Foreach:

  • Ітерації відбуваються послідовно, одна за одною
  • петля foreach запускається з однієї нитки.
  • цикл foreach визначається у всіх рамках .NET
  • Виконання повільних процесів може бути повільнішим , оскільки вони виконуються послідовно
    • Процес 2 не може розпочатися, поки не буде виконано 1. Процес 3 не може розпочатися, доки 2 і 1 не будуть виконані ...
  • Виконання швидких процесів може бути швидшим , оскільки немає нарізних накладних витрат

Паралельно.Для кожного:

  • Виконання відбувається паралельно.
  • Parallel.ForEach використовує кілька ниток.
  • Parallel.ForEach визначений у .Net 4.0 та вище.
  • Виконання повільних процесів може бути швидшим , оскільки вони можуть працювати паралельно
    • Процеси 1, 2 і 3 можуть працювати одночасно (див. Повторно використані потоки, наприклад, нижче)
  • Виконання швидких процесів може бути повільнішим , через додаткові нарізки

Наступний приклад наочно демонструє різницю між традиційним циклом форекс і

Приклад 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();
        }
    }
}

Вихідні дані

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

Використовуючи приклад 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

63
Я не погоджуюся з вашою "твердженням" про те, що Parallel.ForEach (завжди) швидший. Це дійсно залежить від тяжкості операції всередині циклу. Це може бути, а може і не варто витрачати на введення паралелізму.
Мартао

1
Ну, паралель для кожного означає, що окремі потоки встановлені для виконання коду в тілі циклу. Незважаючи на те, що .NET має ефективний механізм для цього, це значні витрати. Отже, якщо вам доведеться просто виконувати просту операцію (наприклад, суму чи множення), паралельне передбачення не повинно бути швидшим.
Мартао

3
@Jignesh це навіть не хороший приклад вимірювання, тому я б взагалі не посилався на це. Видаліть "Thread.Sleep (10);" від кожного корпусу циклу і спробуйте його ще раз.
stenly

1
@Martao має рацію, проблема полягає в накладних блокуванні об'єктів, де паралельний підхід може бути довшим, ніж послідовний.
stenly

8
@stenly Я думаю, що сон - це саме причина, чому він є хорошим прикладом. Ви б не використовували PFE з швидкими одиночними ітераціями (як пояснив Мартао) - тому ця відповідь робить ітерацію повільною, і (правильна) перевага PFE підкреслюється. Я згоден, хоча це потрібно пояснити у відповіді, сміливий "завжди швидше" дуже вводить в оману.
мафу

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

Це призведе до розбору рядків паралельно, всередині циклу. Якщо ви хочете більш детального, менш "орієнтованого" вступу до класу Паралель, я написав серію на TPL, яка включає розділ про Parallel.ForEach .


9

Для великих файлів використовуйте наступний код (ви менше голодні пам'яті)

Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line => {
    //Your stuff
});

2

Ці рядки спрацювали для мене.

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.