Чтение онлайн

ЖАНРЫ

C# 4.0 полное руководство - 2011

Шилдт Герберт

Шрифт:

– 5 -6 -1 -2 -3 -4

Как видите, в том потоке, где поиск выполнялся в верхней части массива, отрицательные значения -5 и– 6 были обнаружены раньше, чем значение -1 в том потоке, где поиск происходил в нижней части массива. Следует, однако, иметь в виду, что из-за отличий в степени загрузки задачами, количества доступных процессоров и прочих факторов системного характера могут быть получены разные результаты. А самое главное, что результирующая последовательность совсем не обязательно будет отражать порядок формирования исходной последовательности.

Применение метода AsOrdered

Как отмечалось в предыдущем разделе, по умолчанию порядок формирования результирующей последовательности

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

public static ParallelQuery AsOrdered(this ParallelQuery source) public static ParallelQuery<TSource>

AsOrdered<TSource>(this ParallelQuery<TSource> source)

где TSource обозначает тип элементов в источнике данных source. Метод AsOrdered можно вызывать только для объекта типа ParallelQuery, поскольку он является методом расширения класса ParallelQuery.

Для того чтобы посмотреть, к какому результату может привести применение метода AsOrdered , подставьте его вызов в приведенный ниже запрос из предыдущего примера программы.

// Использовать метод AsOrdered для сохранения порядка // в результирующей последовательности.

var negatives = from val in data.AsParallel.AsOrdered where val < 0 select val;

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

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

Параллельный запрос отменяется таким же образом, как и задача. И в том и в другом случае отмена опирается на структуру 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.

Поделиться с друзьями: