Урок 3. Параллельный цикл ForEach


В третьей части учебника по параллельному программированию в .NET рассматривается параллельный цикл ForEach. Он повторяет элементы в коллекции, выполняя один и тот же код для каждого из них с возможностью параллельной декомпозиции и обработки данных.

Параллельный цикл

Параллельная библиотека задач (TPL) включает в себя две команды цикла, имитирующие циклы for и foreach, за исключением того, что итерации могут быть разложены и выполнены параллельно на машинах с несколькими ядрами. В предыдущей статье мы рассмотрели параллельный цикл for. В этой статье мы рассмотрим параллельный цикл ForEach. Если вы еще не читали предыдущую статью, вам следует подумать об этом, поскольку подводные камни для этих двух циклов одинаковы.

Parallel.ForEach

Параллельный цикл ForEach предоставляет параллельную версию стандартного последовательного цикла foreach. Каждая итерация обрабатывает один элемент из коллекции. Однако параллельный характер цикла означает, что несколько итераций могут выполняться одновременно на разных процессорах или ядрах процессора. Это открывает возможность возникновения проблем синхронизации, поэтому цикл идеально подходит для процессов, где каждая итерация независима от других.

Чтобы продемонстрировать использование параллельного цикла ForEach и его синтаксиса, создайте новое консольное приложение. Добавьте следующую директиву using в автоматически сгенерированный класс, так как метод loop находится в System.Threading.Tasks пространстве имен.

using System.Threading.Tasks;
Для начала мы создадим последовательный цикл foreach, который выполняет длительную задачу один раз для каждого элемента в коллекции. Для этого мы создадим функциональность, аналогичную описанной в предыдущей статье. Приведенный ниже код перебирает список из десяти целых чисел, генерируемых с помощью Enumerable.Range. Вычисление выполняется во время каждой итерации, и число из коллекции выводится вместе с результатом этого вычисления. Если программа выполняется слишком быстро или слишком медленно, отрегулируйте верхний диапазон цикла в методе GetTotal, чтобы достичь приемлемой скорости.

static void Main(string[] args)
{
    foreach (int i in Enumerable.Range(1, 10))
    {
        Console.WriteLine("{0} - {1}", i, GetTotal());
    }
}
 
static long GetTotal()
{
    long total = 0;
    for (int i = 1; i < 1000000000; i++) // отрегулируйте этот цикл в соответствии с
    { 					//со скоростью вашего компьютера
        total += i;    
}
    return total;
}
 
 
/* Вывод
 
1 - 499999999500000000
2 - 499999999500000000
3 - 499999995000000000
4 - 499999995000000000
5 - 499999995000000000
6 - 499999995000000000
7 - 499999995000000000
8 - 499999995000000000
9 - 499999995000000000
10 - 499999999500000000
 
*/
Параллельная версия цикла использует статический метод ForEach класса Parallel. Существует много перегруженных версий этого метода, но в этой статье мы будем использовать только самый простой, которые имеет два аргумента. Первый - это набор объектов, которые будут перечислены. Это может быть любая коллекция, реализующая IEnumerable.

Второй параметр принимает делегат действия, обычно выражаемый в виде лямбда-выражения, которое определяет действие, выполняемое для каждого элемента коллекции. Параметр делегата содержит элемент из коллекции, который должен быть обработан во время итерации.

Приведенный ниже код показывает параллельную версию более раннего последовательного цикла. Обратите внимание, что тело цикла теперь является телом оператора lambda, а коллекция передается первому параметру метода.

Parallel.ForEach(Enumerable.Range(1, 10), i => 
{
    Console.WriteLine("{0} - {1}", i, GetTotal());
});
 
/* OUTPUT
 
2 - 499999999500000000
1 - 499999999500000000
3 - 499999999500000000
5 - 499999999500000000
4 - 499999999500000000
6 - 499999999500000000
9 - 499999999500000000
7 - 499999999500000000
8 - 499999999500000000
10 - 499999999500000000
 
*/
Как вы можете видеть, результаты отдельных итераций идентичны последовательной версии цикла. Однако итерации обычно обрабатываются не последовательно. Вывод из примера показывает порядок выполнения одного цикла на двухъядерном процессоре. Результат вне последовательности обусловлен параллельным характером операции. Порядок будет варьироваться в зависимости от количества доступных процессорных ядер и их текущего использования другим программным обеспечением.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, параллельное программирование, си шарп, циклы, foreach




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Юмористический дневник бросающего курить
Сменил ли C# C++?
Обзор Java Date и Time API