Поделиться через


Сохранение порядка в PLINQ

В PLINQ цель — повысить производительность при сохранении правильности. Запрос должен выполняться как можно быстрее, но по-прежнему получать правильные результаты. В некоторых случаях правильность требует сохранения порядка исходной последовательности; однако порядок может быть дорогостоящим. Поэтому по умолчанию PLINQ не сохраняет порядок исходной последовательности. В этом отношении PLINQ напоминает LINQ to SQL, но в отличие от LINQ to Objects, который сохраняет порядок.

Чтобы переопределить поведение по умолчанию, можно включить сохранение порядка с помощью AsOrdered оператора в исходной последовательности. Затем вы можете отключить сохранение заказа позже в запросе AsUnordered с помощью метода. При использовании обоих методов запрос обрабатывается на основе эвристики, которая определяет, следует ли выполнять запрос как параллельный или последовательный. Дополнительные сведения см. "Понимание ускорения в PLINQ".

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

var cityQuery =
    (from city in cities.AsParallel()
     where city.Population > 10000
     select city).Take(1000);
Dim cityQuery = From city In cities.AsParallel()
                Where city.Population > 10000
                Take (1000)

Этот запрос не обязательно создает первые 1000 городов в исходной последовательности, которые соответствуют условию, а скорее некоторые из 1000 городов, которые соответствуют условию. Операторы запросов PLINQ разбивают исходную последовательность на несколько подпоследовательностей, которые обрабатываются как одновременные задачи. Если сохранение порядка не указано, результаты каждого раздела передаются на следующий этап запроса в произвольном порядке. Кроме того, раздел может предоставить подмножество своих результатов, прежде чем продолжить обработку оставшихся элементов. Результирующий порядок может отличаться каждый раз. Приложение не может контролировать это, так как оно зависит от того, как операционная система планирует потоки.

Следующий пример переопределяет поведение по умолчанию с помощью AsOrdered оператора в исходной последовательности. Это гарантирует, что Take метод возвращает первые 1000 городов в исходной последовательности, удовлетворяющей условию.

var orderedCities =
    (from city in cities.AsParallel().AsOrdered()
     where city.Population > 10000
     select city).Take(1000);

Dim orderedCities = From city In cities.AsParallel().AsOrdered()
                    Where city.Population > 10000
                    Take (1000)

Однако этот запрос, вероятно, не выполняется так быстро, как неупорядоченная версия, так как ему необходимо отслеживать исходное упорядочение через разделы и во время слияния гарантировать согласованность упорядочения. Поэтому рекомендуется использовать AsOrdered только в тех случаях, когда это необходимо, и только для тех частей запроса, которые требуют его. Если сохранение порядка больше не требуется, используйте AsUnordered, чтобы его отключить. В следующем примере это достигается путем создания двух запросов.

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
    {
        city.Name,
        Pop = city.Population,
        c.Mayor
    };

foreach (var city in finalResult) { /*...*/ }
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

Обратите внимание, что PLINQ сохраняет порядок последовательности, созданной операторами, налагающими порядок, в оставшейся части запроса. Другими словами, операторы, такие как OrderBy и ThenBy рассматриваются, как если бы за ними последовал вызов AsOrdered.

Операторы запросов и упорядочивание

Следующие операторы запросов вводят сохранение порядка во все последующие операции в запросе или до вызова AsUnordered:

В некоторых случаях следующие операторы запросов PLINQ могут потребовать упорядоченные исходные последовательности, чтобы получить правильные результаты.

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

Оператор Результат при упорядочении исходной последовательности Результат при отмене порядка исходной последовательности
Aggregate Недетерминированные выходные данные для неассоциативных или некоммутативных операций Недетерминированные выходные данные для неассоциативных или некоммутативных операций
All Неприменимо Неприменимо
Any Неприменимо Неприменимо
AsEnumerable Неприменимо Неприменимо
Average Недетерминированные выходные данные для неассоциативных или некоммутативных операций Недетерминированные выходные данные для неассоциативных или некоммутативных операций
Cast Упорядоченные результаты Неупорядоченные результаты
Concat Упорядоченные результаты Неупорядоченные результаты
Count Неприменимо Неприменимо
DefaultIfEmpty Неприменимо Неприменимо
Distinct Упорядоченные результаты Неупорядоченные результаты
ElementAt Вернуть указанный элемент Произвольный элемент
ElementAtOrDefault Вернуть указанный элемент Произвольный элемент
Except Неупорядоченные результаты Неупорядоченные результаты
First Вернуть указанный элемент Произвольный элемент
FirstOrDefault Вернуть указанный элемент Произвольный элемент
ForAll Выполняется параллельно и недетерминированно Выполняется параллельно и недетерминированно
GroupBy Упорядоченные результаты Неупорядоченные результаты
GroupJoin Упорядоченные результаты Неупорядоченные результаты
Intersect Упорядоченные результаты Неупорядоченные результаты
Join Упорядоченные результаты Неупорядоченные результаты
Last Вернуть указанный элемент Произвольный элемент
LastOrDefault Вернуть указанный элемент Произвольный элемент
LongCount Неприменимо Неприменимо
Min Неприменимо Неприменимо
OrderBy Переупорядочение последовательности Запускает новый упорядоченный раздел
OrderByDescending Переупорядочение последовательности Запускает новый упорядоченный раздел
Range Неприменимо (то же, что и AsParallel по умолчанию) Неприменимо
Repeat Неприменимо (то же, что и AsParallelпо умолчанию) Неприменимо
Reverse Разворачивает Ничего не делает
Select Упорядоченные результаты Неупорядоченные результаты
Select (индексировано) Упорядоченные результаты Неупорядоченные результаты.
SelectMany Упорядоченные результаты. Неупорядоченные результаты
SelectMany (индексировано) Упорядоченные результаты. Неупорядоченные результаты.
SequenceEqual Упорядоченное сравнение Неупорядоченное сравнение
Single Неприменимо Неприменимо
SingleOrDefault Неприменимо Неприменимо
Skip Пропускает первые n-элементы Пропускает любые элементы n
SkipWhile Упорядоченные результаты. Недетерминированным. Выполняет SkipWhile в текущем произвольном порядке
Sum Недетерминированные выходные данные для неассоциативных или некоммутативных операций Недетерминированные выходные данные для неассоциативных или некоммутативных операций
Take Берет первые n элементы Принимает любые n элементы
TakeWhile Упорядоченные результаты Недетерминированным. Выполняет операцию TakeWhile в текущем произвольном порядке
ThenBy Добавки OrderBy Добавки OrderBy
ThenByDescending Добавки OrderBy Добавки OrderBy
ToArray Упорядоченные результаты Неупорядоченные результаты
ToDictionary Неприменимо Неприменимо
ToList Упорядоченные результаты Неупорядоченные результаты
ToLookup Упорядоченные результаты Неупорядоченные результаты
Union Упорядоченные результаты Неупорядоченные результаты
Where Упорядоченные результаты Неупорядоченные результаты
Where (индексировано) Упорядоченные результаты Неупорядоченные результаты
Zip Упорядоченные результаты Неупорядоченные результаты

Неупорядоченные результаты не активно перетасовываются; Они просто не имеют специальной логики упорядочения, примененной к ним. В некоторых случаях неупорядоченный запрос может сохранить порядок исходной последовательности. Для запросов, использующих индексируемый оператор Select, PLINQ гарантирует, что выходные элементы будут выходить в порядке увеличения индексов, но не гарантирует, какие индексы будут назначены элементам.

См. также