Beibehaltung der Reihenfolge in PLINQ
Das Ziel in PLINQ ist, die Leistung zu maximieren und gleichzeitig die korrekte Ausführung sicherzustellen. Eine Abfrage sollte so schnell wie möglich ausgeführt werden, dabei jedoch stets die korrekten Ergebnissen erzeugen. In einigen Fällen muss für eine korrekte Ausführung die Reihenfolge der Quellsequenz beibehalten werden, eine Sortierung kann jedoch sehr rechenintensiv sein. PLINQ behält die Reihenfolge der Quellsequenz daher standardmäßig nicht bei. In dieser Hinsicht ähnelt PLINQ LINQ, unterscheidet sich jedoch von LINQ to-Objekten, wo die Reihenfolge beibehalten wird.
Um das Standardverhalten zu überschreiben, können Sie mithilfe des AsOrdered-Operators in der Quellsequenz die Beibehaltung der Reihenfolge aktivieren. Sie können die Beibehaltung der Reihenfolge später in der Abfrage mit der AsUnordered-Methode deaktivieren. Bei beiden Methoden wird die Abfrage auf Grundlage der Heuristik verarbeitet, die bestimmt, ob die Abfrage parallel oder sequenziell ausgeführt wird. Weitere Informationen finden Sie unter Understanding Speedup in PLINQ (Grundlagen zur Beschleunigung in PLINQ).
Im folgenden Beispiel wird eine ungeordnete parallele Abfrage gezeigt, die alle mit einer Bedingung übereinstimmenden Elemente heraus filtert, ohne die Ergebnisse auf irgendeine Weise zu sortieren.
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)
Diese Abfrage gibt nicht notwendigerweise die ersten 1000 mit der Bedingung übereinstimmenden Orte in der Quellsequenz zurück, sondern einen Satz mit 1000 beliebigen Orten, die die Bedingung erfüllen. PLINQ-Abfrageoperatoren partitionieren die Quellsequenz in mehrere Untersequenzen, die als gleichzeitige Aufgaben verarbeitet werden. Wenn die Beibehaltung der Reihenfolge nicht angegeben wurde, werden die Ergebnisse jeder Partition in willkürlicher Reihenfolge an die nächste Phase der Abfrage übergeben. Darüber hinaus kann eine Partition eine Teilmenge der Ergebnisse ausgeben, bevor die Verarbeitung der restlichen Elemente fortgesetzt wird. Die resultierende Reihenfolge kann dabei jedes Mal anders sein. Die Anwendung kann die Reihenfolge nicht steuern, da diese davon abhängt, wie das Betriebssystem die Threads plant.
Im folgenden Beispiel wird das Standardverhalten mit dem AsOrdered-Operator in der Quellsequenz überschrieben. Dadurch wird sichergestellt, dass die Take-Methode die ersten 1000 Orte in der Quellsequenz zurückgibt, die die Bedingung erfüllen.
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)
Diese Abfrage wird wahrscheinlich nicht so schnell ausgeführt wie die unsortierte Version, da die ursprüngliche Reihenfolge in allen Partitionen nachverfolgt und beim Merge die Konsistenz der Reihenfolge sichergestellt werden muss. Daher empfiehlt es sich, AsOrdered nur bei Bedarf und nur für die erforderlichen Teile der Abfrage zu verwenden. Wenn die Beibehaltung der Reihenfolge nicht mehr notwendig ist, deaktivieren Sie diese mit AsUnordered. Im folgenden Beispiel wird dies erreicht, indem zwei Abfragen verfasst werden.
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
Beachten Sie, dass PLINQ die Reihenfolge einer Sequenz beibehält, die von Operatoren mit erzwungener Reihenfolge für den Rest der Abfrage erzeugt wurde. Anders ausgedrückt werden Operatoren wie OrderBy und ThenBy so behandelt, als ob ihnen ein Aufruf von AsOrdered folgt.
Abfrageoperatoren und Reihenfolge
Die folgenden Abfrageoperatoren aktivieren die Beibehaltung der Reihenfolge für alle nachfolgenden Vorgänge in einer Abfrage bzw. solange, bis AsUnordered aufgerufen wird:
Die folgenden PLINQ-Abfrageoperatoren erfordern in bestimmten Fällen geordnete Quellsequenzen, damit die korrekten Ergebnisse erzeugt werden:
Einige PLINQ-Abfrageoperatoren verhalten sich anders, je nachdem ob die Quellsequenz geordnet oder ungeordnet ist. In der folgenden Tabelle sind diese Operatoren aufgeführt.
Operator | Ergebnis bei geordneter Quellsequenz | Ergebnis bei ungeordneter Quellsequenz |
---|---|---|
Aggregate | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge |
All | Nicht zutreffend | Nicht verfügbar |
Any | Nicht verfügbar | Nicht verfügbar |
AsEnumerable | Nicht verfügbar | Nicht zutreffend |
Average | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge |
Cast | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Concat | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Count | Nicht zutreffend | Nicht verfügbar |
DefaultIfEmpty | Nicht verfügbar | Nicht zutreffend |
Distinct | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
ElementAt | Rückgabe des angegebenen Elements | Willkürliches Element |
ElementAtOrDefault | Rückgabe des angegebenen Elements | Willkürliches Element |
Except | Ungeordnete Ergebnisse | Ungeordnete Ergebnisse |
First | Rückgabe des angegebenen Elements | Willkürliches Element |
FirstOrDefault | Rückgabe des angegebenen Elements | Willkürliches Element |
ForAll | Nicht deterministische, parallele Ausführung | Nicht deterministische, parallele Ausführung |
GroupBy | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
GroupJoin | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Intersect | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Join | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Last | Rückgabe des angegebenen Elements | Willkürliches Element |
LastOrDefault | Rückgabe des angegebenen Elements | Willkürliches Element |
LongCount | Nicht zutreffend | Nicht verfügbar |
Min | Nicht verfügbar | Nicht zutreffend |
OrderBy | Neusortierung der Sequenz | Start eines neuen geordneten Abschnitts |
OrderByDescending | Neusortierung der Sequenz | Start eines neuen geordneten Abschnitts |
Range | Nicht zutreffend (gleicher Standardwert wie AsParallel) | Nicht zutreffend |
Repeat | Nicht zutreffend (gleicher Standardwert wie AsParallel) | Nicht zutreffend |
Reverse | Umkehrung | Keine Auswirkung |
Select | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Select (indiziert) | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
SelectMany | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
SelectMany (indiziert) | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
SequenceEqual | Geordneter Vergleich | Ungeordneter Vergleich |
Single | Nicht zutreffend | Nicht verfügbar |
SingleOrDefault | Nicht verfügbar | Nicht zutreffend |
Skip | Überspringt die ersten n Elemente | Überspringt n Elemente |
SkipWhile | Geordnete Ergebnisse | Nicht deterministisch Ausführung von SkipWhile für die aktuelle willkürliche Reihenfolge |
Sum | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge | Nicht deterministische Ausgabe für nicht assoziative oder nicht kommutative Vorgänge |
Take | Verwendung der ersten n Elemente |
Verwendung aller n Elemente |
TakeWhile | Geordnete Ergebnisse | Nicht deterministisch Ausführung von TakeWhile für die aktuelle willkürliche Reihenfolge |
ThenBy | Ergänzung zu OrderBy |
Ergänzung zu OrderBy |
ThenByDescending | Ergänzung zu OrderBy |
Ergänzung zu OrderBy |
ToArray | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
ToDictionary | Nicht zutreffend | Nicht zutreffend |
ToList | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
ToLookup | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Union | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Where | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Where (indiziert) | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Zip | Geordnete Ergebnisse | Ungeordnete Ergebnisse |
Ungeordnete Ergebnisse werden nicht aktiv gemischt. Auf sie wird lediglich keine bestimmte Sortierlogik angewendet. In einigen Fällen wird in einer ungeordneten Abfrage möglicherweise die Reihenfolge der Quellsequenz beibehalten. Für Abfragen mit dem indizierten Select-Operator stellt PLINQ sicher, dass Elemente in aufsteigender Indexreihenfolge ausgegeben werden, jedoch wird nicht festgelegt, welcher Index welchem Element zugewiesen wird.