Добавить в цитаты Настройки чтения

Страница 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 &lt; 0) pis.Break();</b>

<b>    Console.WriteLine(&quot;Значение: &quot; + v);</b>

<b>  }</b>

<b>  static void Main() {</b>

<b>    Console.WriteLine(&quot;Основной поток запущен.&quot;);</b>

<b>    data = new int[100000000];</b>

<b>    // Инициализировать данные.</b>

<b>    for (int i=0; i &lt; 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(&quot;nЦикл завершился преждевременно из-за того, &quot; +</b>

<b>             &quot;что обнаружено отрицательное значение&quot; +</b>

<b>             &quot;на шаге цикла номер &quot; +</b>

<b>             loopResult.LowestBreakIteration + &quot;.n&quot;);</b>

<b>    Console.WriteLine(&quot;Основной поток завершен.&quot;);</b>

<b>  }</b>

<b>}</b>

В приведенной выше программе именованный метод применяется в качестве делегата, представляющего "тело" цикла. Но иногда удобнее применять анонимный метод. В качестве примера ниже приведено реализуемое в виде лямбда-выражения "тело" цикла, параллельно выполняемого методом ForEach().

<b>// Использовать цикл, параллельно выполняемый методом ForEach(),</b>

<b>// для отображения данных на экране.</b>

<b>ParallelLoopResult loopResult =</b>

<b>        Parallel.ForEach(data, (v, pis) =&gt; {</b>

<b>                Console.WriteLine(&quot;Значение: &quot; + v);</b>

<b>                if (v &lt; 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. Для простых запросов это единственное необходимое условие.