Как я могу преобразовать этот код 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]и так далее.
Джепп Стиг Нильсен

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
Меня смущает, что этот ответ помечен как правильный ответ, поскольку нет оригинального ответа на вопрос «Что такое Parallel.ForEach и для чего он
нужен

6
@fosb Проблема в том, что заголовок вопроса был отредактирован, чтобы полностью изменить смысл ... поэтому этот ответ больше не имеет никакого смысла. Сказав это, это все еще плохой ответ
aw04

274

Цикл по каждому элементу:

  • Итерации происходят последовательно, одна за другой
  • Цикл foreach запускается из одного потока.
  • Цикл foreach определен в каждом фреймворке .NET
  • Выполнение медленных процессов может быть медленнее , так как они запускаются последовательно
    • Процесс 2 не может начаться, пока не будет сделано 1. Процесс 3 не может начаться, пока 2 и 1 не будут сделаны ...
  • Выполнение быстрых процессов может быть более быстрым , так как нет затрат на многопоточность

Parallel.ForEach:

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

Следующий пример ясно демонстрирует разницу между традиционным циклом foreach и

Пример 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 имеет эффективный механизм для этого, это требует значительных затрат. Итак, если вам просто нужно выполнить простую операцию (например, сумму или умножение), параллельный foreach не должен быть быстрее.
Мартао

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

1
@ Мартао прав, проблема в блокировке объектов, где параллельный подход может быть длиннее последовательного.
февраля

8
@ Скорее всего, я думаю, что именно сон является хорошим примером. Вы не будете использовать 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
});

Это приведет к параллельному анализу строк в цикле. Если вы хотите более подробное, менее «ориентированное на ссылки» введение в класс Parallel, я написал серию статей по 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.