Creare variabili locali al thread con il .NET Framework 4.0

di Cristian Civera, in .NET Framework,

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

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

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

Approfondimenti

I più letti di oggi