Freigeben über


Beibehaltung der Reihenfolge in PLINQ

In PLINQ besteht das Ziel darin, die Leistung zu maximieren und gleichzeitig die Korrektheit aufrechtzuerhalten. Eine Abfrage sollte so schnell wie möglich ausgeführt werden, aber dennoch die richtigen Ergebnisse erzielen. In einigen Fällen muss die Reihenfolge der Quellsequenz beibehalten werden; Die Sortierung kann jedoch rechenintensiv sein. Daher behält PLINQ standardmäßig nicht die Reihenfolge der Quellsequenz bei. In diesem Zusammenhang ähnelt PLINQ LINQ to SQL, steht jedoch im Gegensatz zu LINQ to Objects, das die Sortierung bewahrt.

Um das Standardverhalten außer Kraft zu setzen, können Sie die Reihenfolge beibehalten, indem Sie den AsOrdered Operator für die Quellsequenz verwenden. Sie können die Reihenfolgenerhaltung später in der Abfrage mithilfe der AsUnordered Methode deaktivieren. Bei beiden Methoden wird die Abfrage basierend auf den Heuristiken verarbeitet, die bestimmen, ob die Abfrage parallel oder sequenziell ausgeführt werden soll. Weitere Informationen finden Sie unter Understanding Speedup in PLINQ.

Das folgende Beispiel zeigt eine ungeordnete parallele Abfrage, die nach allen Elementen filtert, die einer Bedingung entsprechen, ohne die Ergebnisse auf irgendeine Weise zu ordnen.

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 erzeugt nicht unbedingt die ersten 1000 Städte in der Quellsequenz, die die Bedingung erfüllen, sondern einige von 1000 Städten, die die Bedingung erfüllen. PLINQ-Abfrageoperatoren partitionieren die Quellsequenz in mehrere Untersequencen, die als gleichzeitige Aufgaben verarbeitet werden. Wenn die Erhaltung der Reihenfolge nicht angegeben ist, werden die Ergebnisse jeder Partition an die nächste Phase der Abfrage in einer beliebigen Reihenfolge übergeben. Außerdem kann eine Partition eine Teilmenge ihrer Ergebnisse liefern, bevor sie die verbleibenden Elemente weiter verarbeitet. Die resultierende Reihenfolge kann jedes Mal unterschiedlich sein. Die Anwendung kann dies nicht steuern, da sie davon abhängt, wie das Betriebssystem die Threads plant.

Im folgenden Beispiel wird das Standardverhalten überschrieben, indem der AsOrdered Operator in der Quellsequenz verwendet wird. Dadurch wird sichergestellt, dass die Take Methode die ersten 1000 Städte 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 jedoch wahrscheinlich nicht so schnell ausgeführt wie die ungeordnete Version, da sie die ursprüngliche Sortierung während der gesamten Partitionen nachverfolgen muss und zur Zusammenführungszeit sicherstellen muss, dass die Sortierung konsistent ist. Daher empfiehlt es sich, nur dann zu verwenden AsOrdered , wenn sie erforderlich ist, und nur für die Teile der Abfrage, für die sie erforderlich ist. 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 Reihenfolge erzwingenden Operatoren für den verbleibenden Teil der Abfrage erzeugt wird. Mit anderen Worten, Operatoren wie OrderBy und ThenBy werden behandelt, als ob ihnen ein Aufruf von AsOrdered folgen würde.

Abfrageoperatoren und Sortierung

Mit den folgenden Abfrageoperatoren wird die Reihenfolgeerhaltung in alle nachfolgenden Abfragevorgänge eingeführt, oder bis AsUnordered aufgerufen wird.

Die folgenden PLINQ-Abfrageoperatoren können in einigen Fällen geordnete Quellsequenzen erfordern, um korrekte Ergebnisse zu erzielen:

Einige PLINQ-Abfrageoperatoren verhalten sich je nachdem, ob ihre Quellsequenz sortiert oder ungeordnet ist. In der folgenden Tabelle sind diese Operatoren aufgeführt.

Bediener Ergebnis, wenn die Quellsequenz sortiert wird Ergebnis, wenn die Quellsequenz nicht angeordnet ist
Aggregate Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge
All Nicht anwendbar Nicht anwendbar
Any Nicht anwendbar Nicht anwendbar
AsEnumerable Nicht anwendbar Nicht anwendbar
Average Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge
Cast Sortierte Ergebnisse Ungeordnete Ergebnisse
Concat Sortierte Ergebnisse Ungeordnete Ergebnisse
Count Nicht anwendbar Nicht anwendbar
DefaultIfEmpty Nicht anwendbar Nicht anwendbar
Distinct Sortierte Ergebnisse Ungeordnete Ergebnisse
ElementAt Angegebenes Element zurückgeben Willkürliches Element
ElementAtOrDefault Angegebenes Element zurückgeben Willkürliches Element
Except Ungeordnete Ergebnisse Ungeordnete Ergebnisse
First Angegebenes Element zurückgeben Willkürliches Element
FirstOrDefault Angegebenes Element zurückgeben Willkürliches Element
ForAll Nicht deterministische, parallele Ausführung Nicht deterministische, parallele Ausführung
GroupBy Sortierte Ergebnisse Ungeordnete Ergebnisse
GroupJoin Sortierte Ergebnisse Ungeordnete Ergebnisse
Intersect Sortierte Ergebnisse Ungeordnete Ergebnisse
Join Sortierte Ergebnisse Ungeordnete Ergebnisse
Last Angegebenes Element zurückgeben Willkürliches Element
LastOrDefault Angegebenes Element zurückgeben Willkürliches Element
LongCount Nicht anwendbar Nicht anwendbar
Min Nicht anwendbar Nicht anwendbar
OrderBy Neu anordnen der Sequenz Start eines neuen geordneten Abschnitts
OrderByDescending Neu anordnen der Sequenz Start eines neuen geordneten Abschnitts
Range Nicht anwendbar (gleiche Standardeinstellung wie AsParallel ) Nicht anwendbar
Repeat Nicht anwendbar (gleiche Standardeinstellung wie AsParallel) Nicht anwendbar
Reverse Umkehrungen Tut nichts
Select Sortierte Ergebnisse Ungeordnete Ergebnisse
Select (indiziert) Sortierte Ergebnisse Ungeordnete Ergebnisse.
SelectMany Sortierte Ergebnisse. Ungeordnete Ergebnisse
SelectMany (indiziert) Sortierte Ergebnisse. Ungeordnete Ergebnisse.
SequenceEqual Sortierter Vergleich Ungeordneter Vergleich
Single Nicht anwendbar Nicht anwendbar
SingleOrDefault Nicht anwendbar Nicht anwendbar
Skip Überspringt die ersten n-Elemente . Überspringt alle n-Elemente .
SkipWhile Sortierte Ergebnisse. Nicht deterministisch. Führt SkipWhile in der aktuellen beliebigen Reihenfolge aus.
Sum Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge Nicht deterministische Ausgabe für nicht assoziative oder nichtkommutative Vorgänge
Take Verwendung der ersten n Elemente Verwendung aller n Elemente
TakeWhile Sortierte 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 Sortierte Ergebnisse Ungeordnete Ergebnisse
ToDictionary Nicht anwendbar Nicht anwendbar
ToList Sortierte Ergebnisse Ungeordnete Ergebnisse
ToLookup Sortierte Ergebnisse Ungeordnete Ergebnisse
Union Sortierte Ergebnisse Ungeordnete Ergebnisse
Where Sortierte Ergebnisse Ungeordnete Ergebnisse
Where (indiziert) Sortierte Ergebnisse Ungeordnete Ergebnisse
Zip Sortierte Ergebnisse Ungeordnete Ergebnisse

Ungeordnete Ergebnisse werden nicht aktiv neu angeordnet; sie haben einfach keine spezielle Sortierlogik angewendet. In einigen Fällen kann eine ungeordnete Abfrage 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