Il multithreading e la parallelizzazione sono tematiche che sono state prese molto in considerazione con il .NET Framework 4.0. Oltre al Parallel Framework, vi sono molti ritocchi e nuove funzionalità che già si è avuto modo di vedere con altri script. Una delle esigenze più comuni di cui si necessita quando si lavora con più thread è di mantenere dei valori di una stessa variabile a livello di thread, in modo che ogni thread lavori con oggetti diversi e possa manipolare la variabile come vuole.
Per raggiungere tale risultato, fin dalle prime versioni è possibile utilizzare l'attributo ThreadStatic sul membro della classe. Nel seguente esempio si suppone di eseguire un'azione parallelamente su più per thread, per far scrivere a video l'ID del thread che lo sta eseguendo, assicurandosi che tale valore venga inizializzato una sola volta per thread.
[ThreadStatic()] private static int threadId; static void Main(string[] args) { Action<int> a = i => { if (threadId == 0) threadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Iteration {0} on thread {1}", i, threadId); }; Parallel.For(1, 10, new ParallelOptions(), a); }
Eseguendolo, poiché i core sono due, alcuni thread verranno usati più di una volta e a video verrà stampato il seguente testo.
Iteration 1 on thread 1 Iteration 2 on thread 1 Iteration 3 on thread 1 Iteration 4 on thread 1 Iteration 6 on thread 1 Iteration 7 on thread 1 Iteration 8 on thread 1 Iteration 5 on thread 4 Iteration 9 on thread 3
Lo snippet precedente presenta però alcuni difetti. Prima di tutto si è obbligati a ricorrere ad una classe e ad un campo per poter memorizzare il valore per thread. Inoltre, ad ogni iterazione si è obbligati a controllare che la variabile sia inizializzata e provvedere a farlo qualora sia null o corrisponda ad una costante (in questo caso zero dell'intero). Non vi è altro modo poiché usando l'attributo ThreadStatic non è possibile utilizzare l'inizializzazione inline della variabile:
private static int threadId = Thread.CurrentThread.ManagedThreadId;
Sebbene non comporti alcun errore, il costruttore statico della classe viene eseguito solo una volta e la variabile threadId verrà inizializzata una sola volta.
Per superare questi problemi viene in aiuto la nuova classe ThreadLocal<T>. Il suo utilizzo è molto simile a Lazy<T>, perché permette tramite la proprietà Value di ottenere il valore specifico per ogni thread, e di inizializzarlo attraverso una funzione. Ecco quindi come diventa l'esempio precedente che produrrà il medesimo risultato:
// Variabile locale per thread using (ThreadLocal<Int32> threadIdLocal = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId)) { //Interrogo direttamente la proprietà Value Action<int> a = i => Console.WriteLine("Iteration {0} on thread {1}", i, threadIdLocal.Value); Parallel.For(1, 10, new ParallelOptions(), a); }
Il codice diventa più compatto, più facile da usare ed inoltre è molto performante. La classe ThreadLocal<T> lavora internamente con ThreadStatic e con un sistema a stack sincronizzato che gli permette di controllare e inizializzare facilmente la variabile per ogni thread. E' importante infine chiamare il Dispose dell'oggetto in modo da liberare lo stack per altri utilizzi.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.