Compartir a través de


Conservar el orden en PLINQ

En PLINQ, el objetivo es maximizar el rendimiento al tiempo que se mantiene la corrección. Una consulta debe ejecutarse lo más rápido posible, pero seguir produciendo los resultados correctos. En algunos casos, la corrección requiere que se conserve el orden de la secuencia de origen; sin embargo, la ordenación puede ser computacionalmente costosa. Por lo tanto, de forma predeterminada, PLINQ no conserva el orden de la secuencia de origen. En este sentido, PLINQ se parece a LINQ to SQL, pero es diferente de LINQ to Objects, que mantiene el orden.

Para anular la funcionalidad predeterminada, puede activar la preservación del orden mediante el operador AsOrdered en la secuencia de origen. Después, puede desactivar la conservación del orden más adelante en la consulta mediante el método AsUnordered. Con ambos métodos, la consulta se procesa en función de la heurística que determina si se debe ejecutar la consulta como paralela o como secuencial. Para obtener más información, consulte Comprender la aceleración en PLINQ.

En el ejemplo siguiente se muestra una consulta paralela desordenada que filtra todos los elementos que coinciden con una condición, sin intentar ordenar los resultados de ninguna manera.

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)

Esta consulta no produce necesariamente las primeras 1000 ciudades de la secuencia de origen que cumplen la condición, sino un conjunto de 1000 ciudades que cumplen la condición. Los operadores de consulta PLINQ dividen la secuencia de origen en varias subsecuencias que se procesan como tareas simultáneas. Si no se especifica la preservación del orden, los resultados de cada partición se transfieren a la siguiente etapa de la consulta en un orden arbitrario. Además, una partición puede producir un subconjunto de sus resultados antes de continuar procesando los elementos restantes. El orden resultante puede ser diferente cada vez. La aplicación no puede controlar esto porque depende de cómo programa el sistema operativo los subprocesos.

En el ejemplo siguiente se invalida el comportamiento predeterminado mediante el AsOrdered operador en la secuencia de origen. Esto garantiza que el Take método devuelva las primeras 1000 ciudades de la secuencia de origen que cumplen la condición.

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)

Sin embargo, esta consulta probablemente no se ejecute tan rápido como la versión desordenada porque debe realizar un seguimiento del orden original en todas las particiones y en el momento de la combinación asegurarse de que la ordenación sea coherente. Por lo tanto, se recomienda usar AsOrdered solo cuando sea necesario y solo para las partes de la consulta que lo requieran. Cuando la conservación del orden ya no sea necesaria, use AsUnordered para desactivarla. En el ejemplo siguiente se logra mediante la redacción de dos consultas.

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

Tenga en cuenta que PLINQ conserva la ordenación de una secuencia producida por operadores de imposición de orden para el resto de la consulta. Es decir, los operadores como OrderBy y ThenBy se tratan como si fueran seguidos de una llamada a AsOrdered.

Operadores de consulta y ordenación

Los siguientes operadores de consulta introducen la conservación del orden en todas las operaciones posteriores de una consulta o hasta que se llame a AsUnordered:

En algunos casos, los siguientes operadores de consulta PLINQ pueden requerir secuencias de origen ordenadas para generar resultados correctos:

Algunos operadores de consulta PLINQ se comportan de forma diferente, en función de si su secuencia de origen está ordenada o no ordenada. En la tabla siguiente se enumeran estos operadores.

Operador Resultado cuando se ordena la secuencia de origen Resultado cuando la secuencia de origen no está ordenada
Aggregate Salida no determinista para operaciones no asociativas o no nomutantes Salida no determinista para operaciones no asociativas o no nomutantes
All No aplicable No aplicable
Any No aplicable No aplicable
AsEnumerable No aplicable No aplicable
Average Salida no determinista para operaciones no asociativas o no nomutantes Salida no determinista para operaciones no asociativas o no nomutantes
Cast Resultados ordenados Resultados desordenados
Concat Resultados ordenados Resultados desordenados
Count No aplicable No aplicable
DefaultIfEmpty No aplicable No aplicable
Distinct Resultados ordenados Resultados desordenados
ElementAt Devolver el elemento especificado Elemento arbitrario
ElementAtOrDefault Devolver el elemento especificado Elemento arbitrario
Except Resultados desordenados Resultados desordenados
First Devolver el elemento especificado Elemento arbitrario
FirstOrDefault Devolver el elemento especificado Elemento arbitrario
ForAll Ejecuta de manera no determinista en paralelo Ejecuta de manera no determinista en paralelo
GroupBy Resultados ordenados Resultados desordenados
GroupJoin Resultados ordenados Resultados desordenados
Intersect Resultados ordenados Resultados desordenados
Join Resultados ordenados Resultados desordenados
Last Devolver el elemento especificado Elemento arbitrario
LastOrDefault Devolver el elemento especificado Elemento arbitrario
LongCount No aplicable No aplicable
Min No aplicable No aplicable
OrderBy Reordena la secuencia Inicia una nueva sección ordenada
OrderByDescending Reordena la secuencia Inicia una nueva sección ordenada
Range No aplicable (el mismo valor predeterminado que AsParallel ) No aplicable
Repeat No aplicable (el mismo valor predeterminado que AsParallel) No aplicable
Reverse Invierte el orden No hace nada
Select Resultados ordenados Resultados desordenados
Select (indexado) Resultados ordenados Resultados desordenados.
SelectMany Resultados ordenados. Resultados desordenados
SelectMany (indexado) Resultados ordenados. Resultados desordenados.
SequenceEqual Comparación ordenada Comparación sin ordenar
Single No aplicable No aplicable
SingleOrDefault No aplicable No aplicable
Skip Omite los primeros n elementos Omite los elementos n
SkipWhile Resultados ordenados. No determinista. Ejecuta SkipWhile en el orden arbitrario actual
Sum Salida no determinista para operaciones no asociativas o no nomutantes Salida no determinista para operaciones no asociativas o no nomutantes
Take Toma los n primeros elementos Toma cualquier elemento n
TakeWhile Resultados ordenados No determinista. Ejecuta TakeWhile en el orden arbitrario actual
ThenBy Suplementos OrderBy Suplementos OrderBy
ThenByDescending Suplementos OrderBy Suplementos OrderBy
ToArray Resultados ordenados Resultados desordenados
ToDictionary No aplicable No aplicable
ToList Resultados ordenados Resultados desordenados
ToLookup Resultados ordenados Resultados desordenados
Union Resultados ordenados Resultados desordenados
Where Resultados ordenados Resultados desordenados
Where (indexado) Resultados ordenados Resultados desordenados
Zip Resultados ordenados Resultados desordenados

Los resultados desordenados no se ordenan activamente; simplemente no tienen ninguna lógica de ordenación especial aplicada a ellos. En algunos casos, una consulta desordenada puede conservar la ordenación de la secuencia de origen. En el caso de las consultas que usan el operador Select indexado, PLINQ garantiza que los elementos de salida aparecerán en el orden de aumentar los índices, pero no garantiza qué índices se asignarán a qué elementos.

Consulte también