Сохранение порядка в PLINQ
Цель PLINQ — максимально повысить производительность при сохранении правильности. Запрос должен выполняться как можно быстрее, но по-прежнему выдавать правильные результаты. В некоторых случаях для поддержания правильности требуется сохранить порядок исходной последовательности. Однако для этого могут потребоваться большие затраты компьютерных ресурсов. Таким образом, по умолчанию в PLINQ не сохраняется порядок исходной последовательности. В этом отношении PLINQ напоминает LINQ to SQL, но не похож на запрос LINQ to Objects, в котором порядок сохраняется.
Чтобы переопределить поведение по умолчанию, можно включить сохранение порядка в исходной последовательности с помощью оператора AsOrdered. Сохранение порядка можно отключить позже в запросе с помощью метода AsUnordered<TSource>. Запрос обрабатывается обоими методами на основании эвристики, определяющей необходимость параллельного или последовательного выполнения запроса. Дополнительные сведения см. в разделе Общее представление об ускорении выполнения в PLINQ.
В следующем примере показан неупорядоченный параллельный запрос, который выполняет фильтрацию для всех элементов, соответствующих условию, не пытаясь упорядочить результаты каким-либо образом.
Dim cityQuery = From city In cities.AsParallel()
Where City.Population > 10000
Take (1000)
var cityQuery = (from city in cities.AsParallel()
where city.Population > 10000
select city)
.Take(1000);
В ответ на этот запрос не обязательно выдаются первые 1000 городов в исходной последовательности, которые удовлетворяют условию, вместо этого выдается некоторый набор из 1000 городов, удовлетворяющих условию. Операторы запроса PLINQ разделяют исходную последовательность на несколько подпоследовательностей, которые обрабатываются как одновременные задачи. Если сохранение порядка не задано, результаты из каждой части передаются на следующий этап запроса в произвольном порядке. Кроме того, разделение может привести к созданию подмножества результатов до того, как начнется обработка оставшихся элементов. Полученный порядок может каждый раз отличаться. Приложение не может управлять эти процессом, поскольку оно зависит от того, как операционная система планирует потоки.
В следующем примере переопределяется поведение по умолчанию в исходной последовательности с помощью оператора AsOrdered. Это гарантирует возврат методом Take<TSource> первых 10 городов в исходной последовательности, отвечающих условию.
Dim orderedCities = From city In cities.AsParallel().AsOrdered()
Where City.Population > 10000
Take (1000)
var orderedCities = (from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city)
.Take(1000);
Однако этот запрос, возможно, не выполняется так же быстро, как неупорядоченная версия, поскольку в нем должен отслеживаться исходный порядок во всех частях исходной последовательности и во время слияния обеспечиваться соответствующий порядок. Следовательно, метод AsOrdered рекомендуется использовать только при необходимости и только для тех частей запроса, которые его требуют. Если сохранение порядка больше не требуется, используйте метод AsUnordered<TSource> для его отключения. В следующем примере это достигается путем создания двух запросов.
Dim orderedCities2 = From city In cities.AsParallel().AsOrdered()
Where city.Population > 10000
Select city
Take (1000)
Dim finalResult = From city In orderedCities2.AsUnordered()
Join p In people.AsParallel() On city.Name Equals p.CityName
Select New With {.Name = city.Name, .Pop = city.Population, .Mayor = city.Mayor}
For Each city In finalResult
Console.WriteLine(city.Name & ":" & city.Pop & ":" & city.Mayor)
Next
var orderedCities2 = (from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city)
.Take(1000);
var finalResult = from city in orderedCities2.AsUnordered()
join p in people.AsParallel() on city.Name equals p.CityName into details
from c in details
select new { Name = city.Name, Pop = city.Population, Mayor = c.Mayor };
foreach (var city in finalResult) { /*...*/ }
Обратите внимание, что в PLINQ сохраняется порядок последовательности, созданный с помощью операторов порядка для остальной части запроса. Другими словами, операторы, такие как OrderBy и ThenBy, обрабатываются, как если бы за ними следовал вызов метода AsOrdered.
Операторы запросов и упорядочение
Следующие операторы запросов обеспечивают сохранение порядка во всех последующих операциях в запросе или до тех пор, пока не вызывается метод AsUnordered<TSource>.
Следующие операторы запросов PLINQ могут в некоторых случаях требовать выдачи правильных результатов упорядоченных исходных последовательностей.
Поведение некоторых операторов запросов PLINQ отличается в зависимости от того, является их исходная последовательность упорядоченной или неупорядоченной. Эти операторы перечислены в следующей таблице.
Оператор |
Результат при упорядоченной исходной последовательности |
Результат при неупорядоченной исходной последовательности |
---|---|---|
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
|
Неприменимо |
Неприменимо |
|
Неприменимо |
Неприменимо |
|
Неприменимо |
Неприменимо |
|
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Неприменимо |
Неприменимо |
|
Неприменимо |
Неприменимо |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Неупорядоченные результаты |
Неупорядоченные результаты |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Выполняется недетерминированно параллельно |
Выполняется недетерминированно параллельно |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Возвращает указанный элемент |
Произвольный элемент |
|
Неприменимо |
Неприменимо |
|
Неприменимо |
Неприменимо |
|
Переупорядочивает последовательность |
Запускает новую упорядоченную часть |
|
Переупорядочивает последовательность |
Запускает новую упорядоченную часть |
|
Неприменимо (такое же значение по умолчанию, что и AsParallel) |
Неприменимо |
|
Неприменимо (такое же значение по умолчанию, как AsParallel) |
Неприменимо |
|
Изменяет порядок на обратный |
Не выполняет действий |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Select (индексирован) |
Упорядоченные результаты |
Неупорядоченные результаты |
Упорядоченные результаты |
Неупорядоченные результаты |
|
SelectMany (индексирован) |
Упорядоченные результаты |
Неупорядоченные результаты |
Упорядоченное сравнение |
Неупорядоченное сравнение |
|
Неприменимо |
Неприменимо |
|
Неприменимо |
Неприменимо |
|
Пропускает первые элементы в количестве n |
Пропускает любые элементы в количестве n |
|
Упорядоченные результаты |
Недетерминированный. Выполняет метод SkipWhile для текущего произвольного порядка |
|
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
Недетерминированные выходные данные для неассоциативных или некоммутативных операций |
|
Принимает первые элементы в количестве n |
Принимает любые элементы в количестве n |
|
Упорядоченные результаты |
Недетерминированный. Выполняет метод TakeWhile для текущего произвольного порядка |
|
Дополняет метод OrderBy |
Дополняет метод OrderBy |
|
Дополняет метод OrderBy |
Дополняет метод OrderBy |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Неприменимо |
Неприменимо |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Упорядоченные результаты |
Неупорядоченные результаты |
|
Where (индексирован) |
Упорядоченные результаты |
Неупорядоченные результаты |
Упорядоченные результаты |
Неупорядоченные результаты |
Неупорядоченные результаты активно не перемешиваются, к ним просто не применяется никакая особая логика сортировки. В некоторых случаях неупорядоченный запрос может сохранять порядок исходной последовательности. Для запросов, использующих индексированный оператор Select, PLINQ всегда выводит выходные элементы в порядке возрастания индексов, однако не гарантирует, какие индексы для каких элементов будут назначены.