Freigeben über


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 to SQL, unterscheidet sich jedoch von LINQ to Objects, 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<TSource>-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 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.

Dim cityQuery = From city In cities.AsParallel()
               Where City.Population > 10000
               Take (1000)
var cityQuery = (from city in cities.AsParallel()
                 where city.Population > 10000
                 select city)
                   .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<TSource>-Methode die ersten 10 Orte in der Quellsequenz zurückgibt, die die Bedingung erfüllen.

Dim orderedCities = From city In cities.AsParallel().AsOrdered()
                    Where City.Population > 10000
                    Take (1000)
            var orderedCities = (from city in cities.AsParallel().AsOrdered()
                                 where city.Population > 10000
                                 select city)
                                .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 Zusammenführen 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<TSource>. Im folgenden Beispiel wird dies erreicht, indem zwei Abfragen verfasst werden.

        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

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 { Name = city.Name, Pop = city.Population, Mayor = c.Mayor };

foreach (var city in finalResult) { /*...*/ }

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<TSource> 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<TSource>

Nicht zutreffend

Nicht zutreffend

Any

Nicht zutreffend

Nicht zutreffend

AsEnumerable<TSource>

Nicht zutreffend

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<TResult>

Geordnete Ergebnisse

Ungeordnete Ergebnisse

Concat

Geordnete Ergebnisse

Ungeordnete Ergebnisse

Count

Nicht zutreffend

Nicht zutreffend

DefaultIfEmpty

Nicht zutreffend

Nicht zutreffend

Distinct

Geordnete Ergebnisse

Ungeordnete Ergebnisse

ElementAt<TSource>

Rückgabe des angegebenen Elements

Willkürliches Element

ElementAtOrDefault<TSource>

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<TSource>

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 zutreffend

Min

Nicht zutreffend

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<TResult>

Nicht zutreffend (gleicher Standardwert wie AsParallel)

Nicht zutreffend

Reverse<TSource>

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 zutreffend

SingleOrDefault

Nicht zutreffend

Nicht zutreffend

Skip<TSource>

Überspringen der ersten n Elemente

Überspringen aller 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<TSource>

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

ToTSource[]

Geordnete Ergebnisse

Ungeordnete Ergebnisse

ToDictionary

Nicht zutreffend

Nicht zutreffend

ToList<TSource>

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

Konzepte

Paralleles LINQ (PLINQ)

Parallele Programmierung in .NET Framework