Sdílet prostřednictvím


Zachování objednávek 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ých případech je správnost závislá na zachování pořadí prvotní sekvence; nicméně řazení 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é části jsou předány do další fáze dotazu v libovolném pořadí. Partice může také poskytnout podmnožinu svých 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 sleduje původní řazení v rámci oddílů a při sloučení zajišťuje, 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, které ukládají pořadí, pro zbytek dotazu. Jinými slovy, s operátory, jako jsou OrderBy a ThenBy, se zachází, jako by následoval jejich 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 Není relevantní Není relevantní
Any Není relevantní Není relevantní
AsEnumerable Není relevantní Není relevantní
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 Není relevantní Není relevantní
DefaultIfEmpty Není relevantní Není relevantní
Distinct Seřazené výsledky Neuspořádané výsledky
ElementAt Vraťte zadaný prvek Libovolný prvek
ElementAtOrDefault Vraťte zadaný prvek Libovolný prvek
Except Neuspořádané výsledky Neuspořádané výsledky
First Vraťte zadaný prvek Libovolný prvek
FirstOrDefault Vraťte zadaný prvek Libovolný prvek
ForAll Vykonává se paralelně nedeterministicky. Vykonává se paralelně nedeterministicky.
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 Vraťte zadaný prvek Libovolný prvek
LastOrDefault Vraťte zadaný prvek Libovolný prvek
LongCount Není relevantní Není relevantní
Min Není relevantní Není relevantní
OrderBy Přeuspořádá pořadí. Spustí novou seřazenou sekci.
OrderByDescending Přeuspořádá pořadí. Spustí novou seřazenou sekci.
Range Nepoužitelné (stejné výchozí jako AsParallel ) Není relevantní
Repeat Nepoužitelné (stejné výchozí jako AsParallel) Není relevantní
Reverse Otočí zpět Nedělá nic
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 Uspořádané srovnání Neuspořádané porovnání
Single Není relevantní Není relevantní
SingleOrDefault Není relevantní Není relevantní
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 Vezme první n prvky. Přebírá všechny n prvky.
TakeWhile Seřazené výsledky Nedeterministické. Provede metodu TakeWhile na aktuální libovolné 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 Není relevantní Není relevantní
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é