NET Framework 4.0: Introduzione alla Parallel Extensions for the .NET Framework - PFx

Carissimi ben trovati. Oggi faremo un’introduzione in merito la nuova feature introdotta nella.NET Framework 4.0, conosciuta come “Parallel Extensions for the .NET Framework”.

Prima di entrare nel merito della PFx, lasciatemi fare una premessa riguardo le attuali performance delle CPU dei nostri calcolatori. Risulta noto a tutti noi, che l’obbiettivo dei produttori di microprocessori è quello di realizzare chip aventi delle performance sempre crescenti al fine di soddisfare le richieste degli sviluppatori di software. Da qualche anno a questa parte, i costruttori di CPU hanno adotto una nuova tecnica per aumentare le performance dei microprocessori, immettendo nel mercato le CPU “Multi-core”. L’obiettivo dei processori “Multi-Core” consiste nel dotare la nostra CPU di due o più core al fine di aumentare le perfomance complessive del sistema.

La domanda che è naturale sottoporsi è la seguente: come si ripercuote tale scelta ingegneristica sulle nostre applicazioni? per meglio dire, come si possono aumentare le performance delle nostre applicazioni per sfruttare al meglio i processori multi-core?

La risposta a questa domanda è semplice: occorre parallelizzare le nostre applicazioni al fine di eseguire più porzioni di codice contemporaneamente sui differenti core della CPU. Eseguire più attività in parallelo, tramite l’utilizzo di più Thread, consentirà alla nostra applicazione di conseguire il risultato per cui è stata progettata in un tempo minore. A tal fine, occorre utilizzare i costrutti che il linguaggio ed il Sistema Operativo mettono a nostra disposizione.

La runtime della .NET Framework 3.5 ci propone già una serie di classi e metodi per definire applicazioni multi-thread e regolare la loro sincronizzazione, ma naturalmente si può pretendere di più. La Parallel Extensions for the .NET Framework nasce esattamente per tale scopo, estendere l’attuale livello di supporto per le applicazioni Managed al fine di sfruttare al meglio le architetture “multi-core”.

Finita tale premessa è opportuno introdurre alcuni concetti di base in merito la PFx. Ovviamente com’è possibile immaginare per ognuno degli argomenti di cui sotto, esistono una serie di dettagli ed informazioni che non è possibile trattare in un unico post.

Prima della .NET Framework 4.0 la definizione dei Thread poteva essere effettuata da un programmatore mediante l’uso di svariati metodi, non esitava cioè un unico punto d’accesso per la definizioni delle attività asincrone. Le possibilità erano svariate:

Generalmente in relazione al tipo di attività da compiere: Long Running Job o Short Running Job veniva adottata la classe più consona alla esigenze del caso.

La prima novità che ritroviamo all’interno della PFx è proprio la seguente: i Thread sono definiti da una nuova classe che rappresenta un unico punto d’accesso per tutte le operazioni parallele. Questo non significa che tutti i metodi conosciuti per la stesura delle operazioni asincrone non siano più supportati, semplicemente si vuole definire una nuova entità per tale tipo di operazioni, la quale raggruppa ed introduce nuove feature in questo contesto. Tale classe è conosciuta con il nome di Task.

Andando a “spulciare” all’interno PFx troviamo un nuovo set di Collections denominate Thread-Safe.

Attualmente tutte le Collections che la runtime .NET Framework 3.5 mette a disposizione non sono Thread-Safe, ovvero se due o più Thread cercano di fare accesso contemporaneamente ad uno stesso dato, non viene garantito il risultato ottenuto a fine computazione. La garanzia del risultato è sicuramente un obbiettivo raggiungibile mediante l’uso di monitor o lock, ma in tal caso sarà lo sviluppatore che dovrà curare i dettagli implementativi.

Il namespace System.Collections.Concurrent definisce un nuovo set di Collections Thread-Safe, le quali esulano il programmatore dalle stesure di codice per la sincronizzazione dei Thread durante l’accesso alle Collection. Alcune di queste classi sono:

Per cioè che riguarda le primitive di sincronizzazione sono stati compiuti eccezionali passi in avanti, basti pensare che l’insieme dei costrutti in merito la sincronizzazione dei Thread è stato notevolmente ampliato. Esistono infatti classi del tutto nuove come:

Che introducono una nuova visione in merito la sincronizzazione delle attività. Esistono inoltre una serie di classi che estendono concetti noti come ad esempio:

Queste ultime “rilassano” le condizioni delle omonime classi presenti nella .NET Framework 3.5.  Rilassare le condizioni per la sincronizzazione significa ad esempio per la classe SemaphoreSlim definire un numero massimo di Thread attivi su una certa regione critica contemporaneamente.

Prima di lasciarvi, vorrei riflettere su un costrutto tipico di tutti i linguaggi di programmazione: i loop.

La riflessione da fare è la seguente: “è possibile parallelizzare un loop?” Non esiste una risposta valida per tutti gli scenari,ma sicuramente si può pensare a parallelizzare un loop in tutte quelle condizioni in cui le iterazioni non sono relazionate l’una all’altra. La Pfx introduce a tal fine una nuova classe: Parallel costruita sfruttando la libreria Task.

Tale classe ci mette a disposizione una serie di metodi statici per parallelizzare i loop come:

  • Parallel.For Method
  • Parallel.ForEach

Come dicevo in precedenza ci sarebbe veramente tanto da scrivere, per cui è possibile che creerò alcuni post specifici per ogni argomento. Fino ad allora, considerate comunque la nostra documentazione on line Wink

Alla prossima.

Carmelo Pulvirenti
Support Engineer
Windows Mobile & Embedded Developer Support
.NET & Visual Studio Technology