Zachování pořadí v PLINQ

V PLINQ je cílem maximalizovat výkon při zachování správnosti. Dotaz by se měl spustit co nejrychleji, ale přesto vytvořit správné výsledky. V některýchpřípadechch Řazení ale může být výpočetně nákladné. Ve výchozím nastavení proto PLINQ nezachová pořadí zdrojové sekvence. V tomto ohledu PLINQ připomíná LINQ to SQL, ale je na rozdíl od LINQ to Objects, což zachovává pořadí.

Chcete-li přepsat výchozí chování, můžete zapnout zachování pořadí pomocí AsOrdered operátoru ve zdrojové sekvenci. Uchovávání objednávek pak můžete vypnout později v dotazu pomocí AsUnordered metody. U obou metod se dotaz zpracuje na základě heuristiky, které určují, jestli se má dotaz spustit jako paralelní nebo sekvenční. Další informace naleznete v tématu Principy zrychlení v PLINQ.

Následující příklad ukazuje neuspořádaný paralelní dotaz, který filtruje pro všechny prvky, které odpovídají podmínce, aniž byste se pokusili výsledky uspořádat jakýmkoli způsobem.

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)

Tento dotaz nemusí nutně vytvořit prvních 1 000 měst ve zdrojové sekvenci, která splňují podmínku, ale spíše některé sady 1 000 měst, která splňují podmínku. Operátory dotazu PLINQ rozdělují zdrojová sekvence do několika dílčích sekvencí, které se zpracovávají jako souběžné úlohy. Pokud není zadáno zachování pořadí, výsledky z každého oddílu se předají do další fáze dotazu v libovolném pořadí. Oddíl může také získat podmnožinu výsledků, než bude pokračovat ve zpracování zbývajících prvků. Výsledné pořadí se může pokaždé lišit. Aplikace to nemůže řídit, protože závisí na tom, jak operační systém naplánuje vlákna.

Následující příklad přepíše výchozí chování pomocí operátoru AsOrdered ve zdrojové sekvenci. Tím zajistíte, že Take metoda vrátí prvních 1 000 měst ve zdrojové sekvenci, která splňují podmínku.

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)

Tento dotaz se však pravděpodobně nespustí tak rychle jako neuspořádaná verze, protože musí sledovat původní řazení v rámci oddílů a při sloučení zajistit, aby řazení bylo konzistentní. Proto doporučujeme, abyste ho používali AsOrdered jenom v případě potřeby a pouze pro ty části dotazu, které ho vyžadují. Pokud už se zachování objednávky nevyžaduje, použijte AsUnordered ji k vypnutí. Následující příklad toho dosáhne tak, že vytvoří dva dotazy.

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

Všimněte si, že PLINQ zachovává pořadí sekvence vytvořené operátory uložením pořadí pro zbytek dotazu. Jinými slovy, operátory, jako OrderBy jsou a ThenBy jsou považovány za volání .AsOrdered

Operátory dotazů a řazení

Následující operátory dotazu zavádějí zachování pořadí do všech následných operací v dotazu, nebo dokud AsUnordered se nevolá:

Následující operátory dotazů PLINQ můžou v některých případech vyžadovat seřazené zdrojové sekvence, aby vznikly správné výsledky:

Některé operátory dotazů PLINQ se chovají odlišně v závislosti na tom, jestli je jejich zdrojová sekvence seřazená nebo neuspořádaná. Následující tabulka uvádí tyto operátory.

Operátor Výsledek při seřazení zdrojové sekvence Výsledek v případech, kdy je pořadí zdroje neuspořádané
Aggregate Nedeterministický výstup pro nonassociativní nebo nekomutativní operace Nedeterministický výstup pro nonassociativní nebo nekomutativní operace
All Nelze použít Nelze použít
Any Nelze použít Nelze použít
AsEnumerable Nelze použít Nelze použít
Average Nedeterministický výstup pro nonassociativní nebo nekomutativní operace Nedeterministický výstup pro nonassociativní nebo nekomutativní operace
Cast Seřazené výsledky Neuspořádané výsledky
Concat Seřazené výsledky Neuspořádané výsledky
Count Nelze použít Nelze použít
DefaultIfEmpty Nelze použít Nelze použít
Distinct Seřazené výsledky Neuspořádané výsledky
ElementAt Vrácený zadaný prvek Libovolný prvek
ElementAtOrDefault Vrácený zadaný prvek Libovolný prvek
Except Neuspořádané výsledky Neuspořádané výsledky
First Vrácený zadaný prvek Libovolný prvek
FirstOrDefault Vrácený zadaný prvek Libovolný prvek
ForAll Provádí nedeterministicky paralelně. Provádí nedeterministicky paralelně.
GroupBy Seřazené výsledky Neuspořádané výsledky
GroupJoin Seřazené výsledky Neuspořádané výsledky
Intersect Seřazené výsledky Neuspořádané výsledky
Join Seřazené výsledky Neuspořádané výsledky
Last Vrácený zadaný prvek Libovolný prvek
LastOrDefault Vrácený zadaný prvek Libovolný prvek
LongCount Nelze použít Nelze použít
Min Nelze použít Nelze použít
OrderBy Změní pořadí pořadí. Spustí nový seřazený oddíl.
OrderByDescending Změní pořadí pořadí. Spustí nový seřazený oddíl.
Range Nepoužitelné (stejné výchozí jako AsParallel ) Nelze použít
Repeat Nepoužitelné (stejné výchozí jako AsParallel) Nelze použít
Reverse Obrátí Neprovádí žádnou akci.
Select Seřazené výsledky Neuspořádané výsledky
Select (indexováno) Seřazené výsledky Neuspořádané výsledky
SelectMany Seřazené výsledky Neuspořádané výsledky
SelectMany (indexováno) Seřazené výsledky Neuspořádané výsledky
SequenceEqual Seřazené porovnání Neuspořádané porovnání
Single Nelze použít Nelze použít
SingleOrDefault Nelze použít Nelze použít
Skip Přeskočí první n prvků. Přeskočí všechny n elementy.
SkipWhile Seřazené výsledky Nedeterministické. Provede Skip While u aktuálního libovolného pořadí.
Sum Nedeterministický výstup pro nonassociativní nebo nekomutativní operace Nedeterministický výstup pro nonassociativní nebo nekomutativní operace
Take Přebírá první n prvky. Přebírá všechny n prvky.
TakeWhile Seřazené výsledky Nedeterministické. Provede dobu trvání aktuálního libovolného pořadí.
ThenBy Doplňky OrderBy Doplňky OrderBy
ThenByDescending Doplňky OrderBy Doplňky OrderBy
ToArray Seřazené výsledky Neuspořádané výsledky
ToDictionary Nelze použít Nelze použít
ToList Seřazené výsledky Neuspořádané výsledky
ToLookup Seřazené výsledky Neuspořádané výsledky
Union Seřazené výsledky Neuspořádané výsledky
Where Seřazené výsledky Neuspořádané výsledky
Where (indexováno) Seřazené výsledky Neuspořádané výsledky
Zip Seřazené výsledky Neuspořádané výsledky

Neuspořádané výsledky nejsou aktivně prohazovány; prostě nemají žádnou speciální logiku řazení, která by se na ně použila. V některých případech může neuspořádaný dotaz zachovat pořadí zdrojové sekvence. U dotazů, které používají indexovaný operátor Select, PLINQ zaručuje, že výstupní prvky budou přijít v pořadí zvýšení indexů, ale neposkytuje žádné záruky, které indexy budou přiřazeny k jakým prvkům.

Viz také