Multithreading e parallelismo con il .NET Framework 4.0


Questo articolo è tratto dal capitolo 19 ("Multithreading ed esecuzione parallela") dei libri Visual Basic 2010 - Guida completa per lo sviluppatore e C# 4 - Guida completa per lo sviluppatore, di Daniele Bochicchio, Cristian Civera, Marco De Sanctis, Riccardo Golia, Alessio Leoncini, Marco Leoncini, Stefano Mostarda.

Acquista subito la tua copia ad un prezzo vantaggioso!

Con l'avvento e il diffondersi di macchine multi-core, per poter sfruttare al massimo le potenzialità dell'hardware è necessario strutturare le applicazioni secondo architetture innovative volte, in particolare, a favorire l'uso del parallelismo. Non si tratta di un'operazione semplice ma avere a disposizione strumenti evoluti rappresenta sicuramente un enorme vantaggio, soprattutto quando forniscono a noi sviluppatori la possibilità di concentrarci solo sugli aspetti logici del requisito da soddisfare, come se creassimo semplice codice multithread, senza doverci preoccupare dei dettagli tecnici con cui il parallelismo viene implementato.

All'interno del .NET Framework 4.0 trova spazio una libreria, chiamata Parallel Extensions, che invece è in grado di gestire thread multipli in maniera ottimizzata in base al numero di CPU, così che anche le nostre applicazioni possano effettivamente eseguire codice in parallelo. Essa si compone di due elementi fondamentali:

  • Task Parallel Library (TPL), cioè un insieme di classi, tramite le quali siamo in grado di eseguire in parallelo porzioni di codice personalizzate;
  • Parallel LINQ (PLINQ), ossia una serie di extension method che ci consentono di sfruttare il parallelismo per calcolare il risultato di query di LINQ to Objects.

Lo scopo di questo capitolo è quello di imparare a conoscere questi due strumenti per capire nel dettaglio come sfruttarli al meglio.

La Task Parallel Library

La classe Task appartiene al namespace System.Threading.Tasks e fornisce un'interfaccia evoluta per la creazione e il controllo sull'esecuzione di codice parallelo. Essa basa la sua interfaccia sull'utilizzo dei delegate Action e Func, a seconda del fatto che eseguiamo una procedura o una funzione. L'esempio 1 mostra alcuni casi tipici di utilizzo.

Esempio 1 - VB
' Costruzione di un semplice task
Dim simpleTask = Task.Factory.StartNew(
  Sub()
    Thread.Sleep(1000)
    Console.WriteLine("Ciao da simpleTask")
  End Sub)

' Costruzione di un task con parametro in input
Dim parameterTask = Task.Factory.StartNew(
  Sub(name)
    Thread.Sleep(1000)
    Console.WriteLine("Ciao da parameterTask, {0}", name)
  End Sub, "Marco De Sanctis")

' Costruzione di un task che ritorna un risultato
Dim resultTask = Task.Factory.StartNew(
  Function(inputValue) As Decimal
    Return PerformSomeLongCalculation(inputValue)
  End Function, 5000D)
Esempio 1 - C#
// Costruzione di un semplice task
var simpleTask = Task.Factory.StartNew(() =>
  {
    Thread.Sleep(1000);
    Console.WriteLine("Ciao da simpleTask");
  });

// Costruzione di un task con parametro in input
var parameterTask = Task.Factory.StartNew((name) =>
  {
    Thread.Sleep(1000);
    Console.WriteLine("Ciao da parameterTask, {0}", name);
  }, "Marco De Sanctis");

// Costruzione di un task che ritorna un risultato
var resultTask = Task.Factory.StartNew((inputValue) => 
  PerformSomeLongCalulation(inputValue), 5000D);

La creazione di un task può essere realizzata tramite l'oggetto TaskFactory, a cui possiamo accedere tramite la proprietà statica Task.Factory e, in particolare, sfruttando il metodo StartNew. Come possiamo notare nell'esempio 1, si tratta di un metodo estremamente versatile, grazie al quale possiamo creare istanze di Task che eseguono procedure o funzioni, sia con, sia senza parametri in ingresso.

I task creati in questo modo vengono automaticamente schedulati per l'esecuzione e quindi avviati non appena possibile. In alternativa, possiamo avvalerci del costruttore per generare un'istanza della classe Task e, successivamente, chiamare il metodo Start.

Esempio 2 - VB
' Creazione esplicita di un task
Dim resultTask = New Task(
  Function(inputValue) As Decimal
    Return performSomeLongCalculation(inputValue)
  End Function, 5000D)

' Esecuzione
resultTask.Start()
Esempio 2 - C#
// Creazione esplicita di un Task
var resultTask = new Task((inputValue) => 
  PerformSomeLongCalulation(inputValue), 5000D);

// Esecuzione
resultTask.Start();

Il codice dell'esempio 2 risulta utile quando vogliamo limitarci a istanziare un oggetto Task senza procedere immediatamente all'esecuzione, magari perché la nostra intenzione è quella di passarlo come parametro a un metodo. Negli altri casi, l'utilizzo di TaskFactory comporta le prestazioni migliori.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

Multithreading e parallelismo con il .NET Framework 4.0 1010 3
| Condividi su: Twitter, Facebook, LinkedIn, Google+

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti