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.