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.

Siehe auch