Come lavorare con Parallel LINQ in C #

Language Integrated Query, noto anche come LINQ, è una pipeline di esecuzione di query che aggiunge funzionalità di query ai linguaggi destinati all'ambiente gestito di .Net. Parallel LINQ, o PLINQ, è un motore di esecuzione di query che viene eseguito sull'ambiente gestito di .Net e sfrutta i più processori o core nel sistema del computer per eseguire le query in parallelo. In altre parole, ti consente di ottimizzare le tue query suddividendole in parti in modo da eseguire queste parti in parallelo e quindi aumentare le prestazioni della query.

PLINQ è un'estensione di LINQ ed è stato introdotto come parte di .Net Framework 4. È un motore di esecuzione di query di Microsoft e fa parte della libreria di estensioni parallele. La libreria di estensioni parallele è a sua volta composta da TPL (Task Parallel Library) e PLINQ. Microsoft ha fornito supporto per la programmazione parallela in .Net Framework per sfruttare i vantaggi dei sistemi multi core. Per sfruttare le capacità di programmazione parallela, una nuova classe chiamata Parallel è stata introdotta in .Net Framework 4.

PLINQ è una buona scelta nelle operazioni legate al calcolo. Ma di cosa si tratta e quali sono i problemi che può risolvere? È appropriato utilizzarlo al posto di LINQ ogni volta che è necessario eseguire query sui dati? Discuteremmo di tutto questo tra un momento, ma prima capiamo come funziona PLINQ dietro le quinte. PLINQ funziona partizionando l'origine dei dati o l'input in blocchi che a loro volta vengono eseguiti da thread diversi.

Un po 'di codice adesso

Considera la seguente query LINQ.

var data = from e in employees

           where e.FirstName.StartsWith("J")

           select e;

È possibile convertire facilmente la query precedente in una query PLINQ utilizzando il metodo di estensione AsParallel. Notare che AsParallel è un metodo di estensione della classe System.Linq.ParallelEnumerable.

var data = from e in employees.AsParallel()

           where e.FirstName.StartsWith("J")

           select e;

Se vuoi mantenere l'ordine del risultato della query, puoi sfruttare il metodo AsOrdered.

var data = from e in employees.AsParallel().AsOrdered()

           where e.FirstName.StartsWith("J")

           select e;

È inoltre possibile preservare l'ordine dei dati restituiti come risultato dell'esecuzione della query PLINQ passando QueryOptions.PreserveOrdering come parametro al metodo AsParallel.

var data = from e in employees.AsParallel(QueryOptions.PreserveOrdering)

           where e.FirstName.StartsWith("J")

           select e;

Si noti che l'utilizzo del metodo AsParallel () non è consigliabile su raccolte di piccole dimensioni: sarebbe piuttosto lento rispetto a una normale query. E se volessi forzare il parallelismo? Questo non è consigliato, ma puoi sfruttare il metodo di estensione WithExecutionMode per ottenere ciò. Ecco un esempio che lo illustra.

var data = from e in employees.AsParallel().WithExecutionMode

                (ParallelExecutionMode.ForceParallelism)

           where e.FirstName.StartsWith("J")

           select e;

Tieni presente che ParallelExecutionMode è un'enumerazione disponibile come parte dello spazio dei nomi System.Linq e può avere uno di questi valori: Default e ForceParallelism. Se specifichi Default come parametro per il metodo di estensione WithExecutionMode, PLINQ eseguirà la query in parallelo se è evidente un miglioramento delle prestazioni nell'esecuzione della query in parallelo. In caso contrario, PLINQ eseguirà la query proprio come una query LINQ. Al contrario, se specifichi ForeParallelism come parametro del metodo di estensione WithExecutionMode, PLINQ eseguirà la query in parallelo anche se ciò potrebbe incorrere in una riduzione delle prestazioni.

Come limito il grado di parallelismo?

Dovresti anche essere a conoscenza di un altro concetto correlato: grado di parallelismo. Questo è un numero intero senza segno che denota il numero massimo di processori di cui la tua query PLINQ dovrebbe trarre vantaggio mentre è in esecuzione. In altre parole, il grado di parallelismo è un numero intero che denota il numero massimo di attività che verrebbero eseguite contemporaneamente per elaborare una query.

Per inciso, il valore predefinito del grado di parallelismo è 64, il che implica che PLINQ può sfruttare un massimo di 64 processori nel sistema. Ecco come limitare il grado di parallelismo in PLINQ a due processori nel sistema.

var data = from e in employees.AsParallel().WithDegreeOfParallelism(2)

           where e.FirstName.StartsWith("J")

           select e;

Nota come il numero di processori è stato passato come argomento al metodo WithDegreeofParallelism. È necessario specificare un valore più alto per il grado di parallelismo per migliorare le prestazioni se la query esegue più lavoro non associato al calcolo, ovvero lavoro non associato alla CPU.

Consiglio vivamente di leggere il documento "Patterns of Parallel Programming" di Stephen Toub. Fornisce una discussione approfondita sui modelli di programmazione parallela in .Net.