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