Опубликован: 15.10.2009 | Доступ: свободный | Студентов: 885 / 247 | Оценка: 4.42 / 4.20 | Длительность: 08:22:00
Специальности: Программист
Лекция 2:

Конструкция Parallel.For

< Лекция 1 || Лекция 2: 12 || Лекция 3 >
Аннотация: В этой лекции рассматривается реализация базовых элементов библиотеки Parallel.For и Parallel.Foreach.

Иногда в циклах, используемых в программах, итерации являются независимыми; т.е., ни в одной итерации не используются результаты работы предыдущих итераций. Посредством класса System.Threading.Parallel такие циклы могут быть преобразованы в циклы, в которых каждая итерация потенциально может быть выполнена параллельно при наличии достаточного количества доступных ядер (процессоров). Заметим, что параллельное исполнение циклов, итерации которых не являются независимыми, может привести к некорректным результатам. В этом случае необходимо либо пересмотреть структуру цикла, либо использовать примитивы синхронизации и\или потокобезопасные структуры данных.

В PFX существует несколько вариантов метода For, исполняющихся параллельно. Наиболее часто применяемым является For(Int32, Int32, Action<Int32>). Здесь первый параметр задает начальный индекс, второй параметр - конечный индекс, и третий параметр - делегат, определяющий действие, которое будет выполняться на каждой итерации.

Рассмотрим следующий последовательный цикл на С#:

for (int i = 0; i < N; i++)
{
  results[i] = Compute(i);
}

C помощью PFX и класса System.Threading.Parallel можно распараллелить этот цикл следующим образом:

using System.Threading;
…
Parallel.For(0, N, delegate(int i)
{
results[i] = Compute(i); 
});

Для упрощения кода можно использовать синтаксис лямбда-выражений, введенный в C# 3.0 (Visual Studio 2008):

using System.Threading;
…
Parallel.For(0, N, i =>
{
results[i] = Compute(i); 
});

Теперь итерации этого цикла могут быть выполнены параллельно.

Распараллеливание циклов foreach выполняется аналогичным образом. Рассмотрим цикл на С#:

foreach(MyClass с in data) 
{
  Compute(c);
}

C помощью PFX он распараллеливается следующим образом:

Parallel.Foreach(data, delegate(MyClass с)) 
{
  Compute(c);
}

Упрощенный код с использованием лямбда-выражений выглядит так:

Parallel.Foreach(data, с => 
{
  Compute(c);
});

Использование foreach, как правило, менее эффективно, чем for, поскольку несколько потоков должны иметь доступ к общей обрабатываемой структуре данных, например, списку. Однако, реализация Parallel.ForEach достаточно интеллектуальна, и в ней доступ из нескольких потоков к общей структуре данных происходит достаточно эффективно.

В качестве примера использования Parallel.For рассмотрим задачу перемножения двух матриц. Последовательная реализация этой задачи может выглядеть так:

void  MultiplyMatrices(int size, double[,] m1, double[,] m2, double[,] result)
{
  for (int i = 0; i < size; i++)
  {
    for (int j = 0; j < size; j++)
    {
      result[i,j] = 0;
      for (int k = 0; k < size; k++)
{
  result[i,j] += m1[i,k] * m2[k,j];
}
}
  }
}

Распараллеливание заключается в простой замене внешнего цикла For циклом Parallel.For:

void  MultiplyMatrices(int size, double[,] m1, double[,] m2, double[,] result)
{
  Parallel.For (0; size; i =>
  {
    for (int j = 0; j < size; j++)
    {
      result[i,j] = 0;
      for (int k = 0; k < size; k++)
{
  result[i,j] += m1[i,k] * m2[k,j];
}
}
  });
}

Также можно выполнить распараллеливание внутреннего (по j) цикла, но только для матриц достаточно большого размера. Распараллеливание внешнего цикла дает достаточную степень параллельности, чтобы получить от нее эффект. Необдуманное использование Parallel.For может нанести ущерб производительности, поэтому внутренний цикл оставлен последовательным.

Еще один пример применения Parallel.ForEach - перечисление всех файлов изображений в директории и обработка каждого из них:

foreach(string imagePath in Directory.GetFiles(path, "*.jpg"))
{
  ProcessImage(imagePath);
}

Параллельный вариант выглядит следующим образом:

Parallel.ForEach(Directory.GetFiles(path, "*.jpg"), imagePath =>

{
  ProcessImage(imagePath);
});
< Лекция 1 || Лекция 2: 12 || Лекция 3 >
Максим Полищук
Максим Полищук
"...Изучение и анализ примеров.
В и приведены описания и приложены исходные коды параллельных программ..."
Непонятно что такое - "В и приведены описания" и где именно приведены и приложены исходные коды.