// Использовать объекты типа ParallelLoopResult и ParallelLoopState, а также
// метод Break() вместе с методом ForEach() для параллельного выполнения цикла.
using System;
using System.Threading.Tasks;
class DemoParallelForWithLoopResult {
static int[] data;
// Метод, служащий в качестве тела параллельно выполняемого цикла.
// В данном примере переменной v передается значение элемента массива
// данных, а не индекс этого элемента.
static void DisplayData(int v, ParallelLoopState pis) {
// Прервать цикл при обнаружении отрицательного значения,
if (v < 0) pis.Break();
Console.WriteLine("Значение: " + v);
}
static void Main() {
Console.WriteLine("Основной поток запущен.");
data = new int[100000000];
// Инициализировать данные.
for (int i=0; i < data.Length; i++) data[i] = i;
// Поместить отрицательное значение в массив data,
data[100000] = -10;
// Использовать цикл, параллельно выполняемый методом ForEach(),
// для отображения данных на экране.
ParallelLoopResult loopResult = Parallel.ForEach(data, DisplayData);
// Проверить, завершился ли цикл,
if(!loopResult.IsCompleted)
Console.WriteLine("\nЦикл завершился преждевременно из-за того, " +
"что обнаружено отрицательное значение" +
"на шаге цикла номер " +
loopResult.LowestBreakIteration + ".\n");
Console.WriteLine("Основной поток завершен.");
}
}
В приведенной выше программе именованный метод применяется в качестве делегата, представляющего "тело" цикла. Но иногда удобнее применять анонимный метод. В качестве примера ниже приведено реализуемое в виде лямбда-выражения "тело" цикла, параллельно выполняемого методом ForEach()
.
// Использовать цикл, параллельно выполняемый методом ForEach(),
// для отображения данных на экране.
ParallelLoopResult loopResult =
Parallel.ForEach(data, (v, pis) => {
Console.WriteLine("Значение: " + v);
if (v < 0) pis.Break();
}
);
Исследование возможностей PLINQ
PLINQ представляет собой параллельный вариант языка интегрированных запросов LINQ и тесно связан с библиотекой TPL. PLINQ применяется, главным образом, для достижения параллелизма данных внутри запроса. Как станет ясно из дальнейшего, сделать это совсем не трудно. Как и TPL, тема PLINQ довольно обширна и многогранна, поэтому в этой главе представлены лишь самые основные понятия данного языка.
Класс ParallelEnumerable
Основу PLINQ составляет класс ParallelEnumerable
, определенный в пространстве имен System.Linq
. Это статический класс, в котором определены многие методы расширения, поддерживающие параллельное выполнение операций. По существу, он представляет собой параллельный вариант стандартного для LINQ класса Enumerable. Многие его методы являются расширением класса ParallelQuery
, а некоторые из них возвращают объект типа ParallelQuery
. В классе ParallelQuery
инкапсулируется последовательность операций, поддерживающая параллельное выполнение. Имеются как обобщенный, так и необобщенный варианты данного класса. Мы не будем обращаться к классу ParallelQuery
непосредственно, а воспользуемся несколькими методами класса ParallelEnumerable
. Самый главный из них, метод AsParallel()
, описывается в следующем разделе.
Распараллеливание запроса методом AsParallel()
Едва ли не самым удобным средством PLINQ является возможность просто создавать параллельный запрос. Нужно лишь вызвать метод AsParallel()
для источника данных. Метод AsParallel()
определен в классе ParallelEnumerable
и возвращает источник данных, инкапсулированный в экземпляре объекта типа ParallelQuery
. Это дает возможность поддерживать методы расширения параллельных запросов. После вызова данного метода запрос разделяет источник данных на части и оперирует с каждой из них таким образом, чтобы извлечь максимальную выгоду из распараллеливания. (Если распараллеливание оказывается невозможным или неприемлемым, то запрос, как обычно, выполняется последовательно.) Таким образом, добавления в исходный код единственного вызова метода AsParallel()
оказывается достаточно для того, чтобы превратить последовательный запрос LINQ в параллельный запрос LINQ. Для простых запросов это единственное необходимое условие.
Читать дальше