После выполнения программы порядок следования элементов в результирующей последовательности будет отражать порядок их расположения в исходной последовательности.

Отмена параллельного запроса

Параллельный запрос отменяется таким же образом, как и задача. И в том и в другом случае отмена опирается на структуру CancellationToken, получаемую из класса CancellationTokenSource. Получаемый в итоге признак отмены передается запросу с помощью метода WithCancellation () . Отмена параллельного запроса производится методом Cancel () , который вызывается для источника признаков отмены. Главное отличие отмены параллельного запроса от отмены задачи состоит в следующем: когда параллельный запрос отменяется, он генерирует исключение OperationCanceledException, а не AggregateException. Но в тех случаях, когда запрос способен сгенерировать несколько исключений, исключение OperationCanceledException может быть объединено в совокупное исключение AggregateException. Поэтому отслеживать лучше оба вида исключений.

Ниже приведена форма объявления метода WithCancellation ():

public static ParallelQuery<TSource>

WithCancellation<TSource> (

this ParallelQuery<TSource> source,

CancellationToken cancellationToken)

где source обозначает вызывающий запрос, a cancellationToken — признак отмены. Этот метод возвращает запрос, поддерживающий указанный признак отмены.

В приведенном ниже примере программы демонстрируется порядок отмены параллельного запроса, сформированного в программе из предыдущего примера. В данной программе организуется отдельная задача, которая ожидает в течение 100 миллисекунд, а затем отменяет запрос. Отдельная задача требуется потому, что цикл foreach, в котором выполняется запрос, блокирует выполнение метода Main () до завершения цикла.

Using System.Linq; using System.Threading; using System.Threading.Tasks;

class PLINQCancelDemo {

static void Main() {

CancellationTokenSource cancelTokSrc = new CancellationTokenSource(); int[] data = new int[10000000];

// Инициализировать массив данных положительными значениями, for (int i=0; i < data.Length; i++) data[i] = i;

//А теперь ввести в массив данных ряд отрицательных значений, data[1000] = -1; data [14000] = -2; data[15000] = -3;

data[676000] = -4; ч

data[8024540] = -5; data [9908000] = -6;

// Использовать запрос PLINQ для поиска отрицательных значений, var negatives = from val in data.AsParallel().

WithCancellation(cancelTokSrc.Token) where val < 0 select val;

// Создать задачу для отмены запроса по истечении 100 миллисекунд.

Task cancelTsk = Task.Factory.StartNew( () => {

Thread.Sleep(100); • cancelTokSrc.Cancel();

});

try {

Foreach(var v in negatives)

Console.Write(v + " ");

} catch(OperationCanceledException exc) {

Console.WriteLine(exc.Message);

} catch(AggregateException exc) {

Console.WriteLine (exc);

} finally {

CancelTsk.Wait (); cancelTokSrc.Dispose(); cancelTsk.Dispose();

}

Console.WriteLine();

}

}

Ниже приведен результат выполнения этой программы. Если запрос отменяется до его завершения, то на экран выводится только сообщение об исключительной ситуации.

Запрос отменен с помощью маркера, переданного в метод WithCancellation.

Другие средства PLINQ

Как упоминалось ранее, PLINQ представляет собой довольно крупную подсистему. Это объясняется отчасти той гибкостью, которой обладает PLINQ. В PLINQ доступны и многие другие средства, помогающие подстраивать параллельные запросы под конкретную ситуацию. Так, при вызове метода WithDegreeOf Parallelism () можно указать максимальное количество процессоров, выделяемых для обработки запроса, а при вызове метода As Sequential ( ) — запросить последовательное выполнение части параллельного запроса. Если вызывающий поток, ожидающий результатов от цикла foreach, не требуется блокировать, то для этой цели можно воспользоваться методом ForAll (). Все эти методы определены в классе ParallelEnumerable. А в тех случаях, когда PLINQ должен по умолчанию поддерживать последовательное выполнение, можно воспользоваться методом WithExecutionMode () , передав ему в качестве параметра признак ParallelExecutionMode . ForceParallelism.

 

Вопросы эффективности PLINQ

Далеко не все запросы выполняются быстрее только потому, что они распараллелены. Как пояснялось ранее в отношении TPL, издержки, связанные с созданием параллельных потоков и управлением их исполнением, могут "перекрыть" все преимущества, которые дает распараллеливание. Вообще говоря, если источник данных оказывается довольно мелким, а требующаяся обработка данных — очень короткой, то внедрение параллелизма может и не привести к ускорению обработки запроса. Поэтому за рекомендациями по данному вопросу следует обращаться к информации корпорации Microsoft.