orderby num descending

    select num).ToArray();

  // Вывести количество найденных чисел

  Console.WriteLine($"Found {modThreeIsZero.Count()} numbers

                      that match query!");

}

<p id="AutBody_Root565">Создание запроса PLINQ</p>

Чтобы проинформировать библиотеку TPL о выполнении запроса в параллельном режиме (если такое возможно), необходимо использовать расширяющий метод AsParallel():

int[] modThreeIsZero = (

  from num in source.AsParallel()

  where num % 3 == 0

  orderby num descending select num).ToArray();

Обратите внимание, что общий формат запроса LINQ идентичен тому, что вы видели в предыдущих главах. Тем не менее, за счет включения вызова AsParallel() библиотека TPL попытается распределить рабочую нагрузку по доступным процессорам. 

<p id="AutBody_Root566">Отмена запроса PLINQ</p>

 С помощью объекта CancellationTokenSource запрос PLINQ можно также информировать о прекращении обработки при определенных условиях (обычно из-за вмешательства пользователя). Объявите на уровне класса Program объект CancellationTokenSource по имени _cancelToken и модифицируйте операторы верхнего уровня для принятия ввода от пользователя. Ниже показаны соответствующие изменения в коде:

CancellationTokenSource _cancelToken =

  new CancellationTokenSource();

do

{

  Console.WriteLine("Start any key to start processing");

                  // Нажмите любую клавишу, чтобы начать обработку

  Console.ReadKey();

  Console.WriteLine("Processing");

  Task.Factory.StartNew(ProcessIntData);

  Console.Write("Enter Q to quit: ");

              // Введите Q для выхода:

  string answer = Console.ReadLine();

  // Желает ли пользователь выйти?

  if (answer.Equals("Q",

    StringComparison.OrdinalIgnoreCase))

  {

    _cancelToken.Cancel();

    break;

  }

}

while (true);

Console.ReadLine();

Теперь запрос PLINQ необходимо информировать о том, что он должен ожидать входящего запроса на отмену выполнения, добавив в цепочку вызов расширяющего метода WithCancellation() с передачей ему маркера отмены. Кроме того, этот запрос PLINQ понадобится поместить в подходящий блок try/catch и обработать возможные исключения. Финальная версия метода ProcessInData() выглядит следующим образом:

void ProcessIntData()

{

  // Получить очень большой массив целых чисел.

  int[] source = Enumerable.Range(1, 10_000_000).ToArray();

  // Найти числа, для которых истинно условие num % 3 == 0,

  // и возвратить их в убывающем порядке.

  int[] modThreeIsZero = null;

  try

  {

    modThreeIsZero =

      (from num in source.AsParallel().WithCancellation(_cancelToken.Token)

            where num % 3 == 0

            orderby num descending

            select num).ToArray();

    Console.WriteLine();

    // Вывести количество найденных чисел.

    Console.WriteLine($"Found {modThreeIsZero.Count()} numbers

                        that match query!");

  }

  catch (OperationCanceledException ex)

  {

    Console.WriteLine(ex.Message);

  }

}

Во время выполнения метода ProcessIntData() понадобится нажать <Q> и быстро произвести ввод, чтобы увидеть сообщение от маркера отмены.

<p id="AutBody_Root567">Асинхронные вызовы с помощью async/await</p>

В этой довольно длинной главе было представлено много материала в сжатом виде. Конечно, построение, отладка и понимание сложных многопоточных приложений требует прикладывания усилий в любой инфраструктуре. Хотя TPL, PLINQ и тип делегата могут до некоторой степени упростить решение (особенно по сравнению с другими платформами и языками), разработчики по-прежнему должны хорошо знать детали разнообразных расширенных приемов.

Перейти на страницу:

Похожие книги