Partager via


Conservation de l'ordre en PLINQ

En PLINQ, l'objectif est de maximiser les performances tout en maintenant l'exactitude. Une requête doit s'exécuter aussi rapidement que possible mais doit produire des résultats corrects. Dans certains cas, l'exactitude requiert que l'ordre de la séquence source soit conservé ; toutefois, le classement sollicite fortement les ressources informatiques. Par conséquent, PLINQ ne conserve pas par défaut l'ordre de la séquence source. À cet égard, PLINQ est similaire à LINQ to SQL, mais contrairement à LINQ to Objects, il ne conserve pas le classement.

Pour substituer le comportement par défaut, vous pouvez activer la conservation de l'ordre en utilisant l'opérateur AsOrdered sur la séquence source. Vous pouvez désactiver ultérieurement la conservation de l'ordre dans la requête à l'aide de la méthode AsUnordered<TSource>. Avec les deux méthodes, la requête est traitée en fonction des paramètres heuristiques qui déterminent si la requête doit être exécutée de manière parallèle ou séquentielle. Pour plus d'informations, consultez Fonctionnement de l'accélération dans PLINQ.

L'exemple suivant affiche une requête parallèle non classée qui filtre tous les éléments qui correspondent à une condition, sans essayer de classer les résultats.

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);

Cette requête ne renvoie pas nécessairement les 1000 premières villes de la séquence source qui remplissent la condition, mais plutôt un ensemble de 1000 villes qui remplissent la condition. Les opérateurs de requête PLINQ partitionnent la séquence source en plusieurs sous-séquences traitées comme des tâches simultanées. Si la conservation de l'ordre n'est pas spécifiée, les résultats de chaque partition sont donnés à l'étape suivante de la requête dans un ordre arbitraire. De plus, une partition peut transmettre un sous-ensemble de ses résultats avant de continuer le traitement des éléments restants. L'ordre résultant peut être différent à chaque fois. Votre application ne peut pas contrôler cela parce cela dépend de la manière dont le système d'exploitation planifie les threads.

L'exemple suivant substitue le comportement par défaut en utilisant l'opérateur AsOrdered sur la séquence source. Cela vérifie que la méthode Take<TSource> retourne les 10 premières villes de la séquence source qui remplissent la condition.

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);

Toutefois, cette requête ne s'exécute pas aussi rapidement que la version non classée probablement parce qu'elle doit effectuer le suivi du classement d'origine dans toutes les partitions et vérifier que le classement est cohérent au moment de la fusion. Par conséquent, il est recommandé d'utiliser AsOrdered uniquement lorsque cela est obligatoire et uniquement pour les parties de la requête qui le nécessitent. Lorsque la conservation de l'ordre n'est plus obligatoire, utilisez AsUnordered<TSource> pour la désactiver. L'exemple suivant effectue ceci en composant deux requêtes.

        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) { /*...*/ }

Notez que PLINQ conserve le classement d'une séquence produit par les opérateurs d'ordre pour le reste de la requête. En d'autres termes, les opérateurs tels que OrderBy et ThenBy sont traités comme s'ils étaient suivis d'un appel à AsOrdered.

Opérateurs de requête et classement

Les opérateurs de requête suivants présentent la conservation de l'ordre dans toutes les opérations suivantes d'une requête ou jusqu'à ce que AsUnordered<TSource> soit appelé :

Les opérateurs de requête PLINQ suivants peuvent nécessiter dans certains cas que les séquences sources classées produisent des résultats corrects :

Certains opérateurs de requête PLINQ se comportent différemment, selon que leur séquence source est classée ou non. Le tableau suivant répertorie ces opérateurs.

Opérateur

Résultat si la séquence source est classée

Résultat si la séquence source n'est pas classée

Aggregate

Sortie non déterministe pour les opérations non associatives ou non commutatives

Sortie non déterministe pour les opérations non associatives ou non commutatives

All<TSource>

Non applicable

Non applicable

Any

Non applicable

Non applicable

AsEnumerable<TSource>

Non applicable

Non applicable

Average

Sortie non déterministe pour les opérations non associatives ou non commutatives

Sortie non déterministe pour les opérations non associatives ou non commutatives

Cast<TResult>

Résultats classés

Résultats non classés

Concat

Résultats classés

Résultats non classés

Count

Non applicable

Non applicable

DefaultIfEmpty

Non applicable

Non applicable

Distinct

Résultats classés

Résultats non classés

ElementAt<TSource>

Retourne l'élément spécifié

Élément arbitraire

ElementAtOrDefault<TSource>

Retourne l'élément spécifié

Élément arbitraire

Except

Résultats non classés

Résultats non classés

First

Retourne l'élément spécifié

Élément arbitraire

FirstOrDefault

Retourne l'élément spécifié

Élément arbitraire

ForAll<TSource>

Exécute en parallèle de manière non déterministe

Exécute en parallèle de manière non déterministe

GroupBy

Résultats classés

Résultats non classés

GroupJoin

Résultats classés

Résultats non classés

Intersect

Résultats classés

Résultats non classés

Join

Résultats classés

Résultats non classés

Last

Retourne l'élément spécifié

Élément arbitraire

LastOrDefault

Retourne l'élément spécifié

Élément arbitraire

LongCount

Non applicable

Non applicable

Min

Non applicable

Non applicable

OrderBy

Réorganise la séquence

Démarre la nouvelle section classée

OrderByDescending

Réorganise la séquence

Démarre la nouvelle section classée

Range

Non applicable (même valeur par défaut que AsParallel)

Non applicable

Repeat<TResult>

Non applicable (même valeur par défaut que AsParallel)

Non applicable

Reverse<TSource>

Inverse

Sans effet

Select

Résultats classés

Résultats non classés

Select (indexé)

Résultats classés

Résultats non classés

SelectMany

Résultats classés

Résultats non classés

SelectMany (indexé)

Résultats classés

Résultats non classés

SequenceEqual

Comparaison classée

Comparaison non classée

Single

Non applicable

Non applicable

SingleOrDefault

Non applicable

Non applicable

Skip<TSource>

Ignore en premier les éléments n

Ignore tous les éléments n

SkipWhile

Résultats classés

Non déterministe Exécute SkipWhile sur l'ordre arbitraire actuel

Sum

Sortie non déterministe pour les opérations non associatives ou non commutatives

Sortie non déterministe pour les opérations non associatives ou non commutatives

Take<TSource>

Prend en premier les éléments n

Prend tous les éléments n

TakeWhile

Résultats classés

Non déterministe Exécute TakeWhile sur l'ordre arbitraire actuel

ThenBy

Complète OrderBy

Complète OrderBy

ThenByDescending

Complète OrderBy

Complète OrderBy

ToTSource[]

Résultats classés

Résultats non classés

ToDictionary

Non applicable

Non applicable

ToList<TSource>

Résultats classés

Résultats non classés

ToLookup

Résultats classés

Résultats non classés

Union

Résultats classés

Résultats non classés

Where

Résultats classés

Résultats non classés

Where (indexé)

Résultats classés

Résultats non classés

Zip

Résultats classés

Résultats non classés

Les résultats non classés ne sont pas lus activement de manière aléatoire ; tout simplement, aucune logique de tri spéciale ne leur est appliquée. Dans certains cas, une requête non classée peut conserver le classement de la séquence source. Pour les requêtes qui utilisent l'opérateur Select indexé, PLINQ garantit que les résultats seront générés dans l'ordre croissant des index, mais n'offre aucune garantie sur la nature des index affectés aux résultats.

Voir aussi

Concepts

Parallel LINQ (PLINQ)

Programmation parallèle dans le .NET Framework