Capire come [[sincronizzazione dei thread in C#]] è indispensabile per poter costruire applicazioni veloci e thread-safe; e in questo contesto è necessario avere chiara la differenza tra blocking e spinning.

Spesso può essere utile mettere in pausa un Thread in modo da aspettare che una determinata condizione avvenga, per ottenere questo ci sono due modi: il blocking e lo spinning.

Tl;Dr: il blocking rilascia il processore in modo che esso possa dedicarsi ad altri thread, mentre lo spinning lancia un loop che tiene il processore occupato sul thread corrente.

Yielding e context switch

Un Thread bloccato restituisce subito la sua gestione al processore e non consuma più risorse fino a che la condizione non è stata rispettata.

Per fare queste operazioni quando un Thread si blocca o si sblocca il sistema operativo opera quello definito come context switch che porta un overhead di qualche microsecondo.

Lo yielding di un thread è l’operazione per cui un thread informa lo scheduler che esso rilascia la slot di tempo assegnata, sarà il sistema operativo a scegliere a quale thread passare. Se non ho alcun thread rimarrò nel thread corrente.

In C# lo yielding può essere chiamato esplicitamente con l’istruzione Thread.Yield().

Blocking

Il modo più comune è il blocking che consiste nel fermare un thread nell’attesa di un segnale (signaling) o che si liberi una risorsa (locking).

Esempi di classi per il signaling sono i ManualResetEvent o AutoResetEvent, poi ci sono anche classi secondarie come il Countdownevent o Barrier, entrambi introdotti in .NET 4.

Il costrutto principale per il locking invece è appunto il lock che permette di ottenere accesso esclusivo ad un metodo bloccando tutti gli altri fino che questo ultimo non viene rilasciato.

Alternative al lock sono la classe Mutex e la classe Semaphore.

Spinning

Lo spinning è invece la modalità di blocco di un thread per cui questo rimane bloccato in una attività CPU intensive senza alcun context switch.

L’esempio classico nel blocco con spinning è un ciclo while senza corpo, per esempio:

while (!condition);

Questo tipo di blocco è estremamente CPU-intensive in quanto la CLR (e conseguentemente il sistema operativo) continuano a effettuare calcoli e controlli alla massima velocità possibile.

Spesso infatti si utilizza un ibrido tra blocking e spinning utilizzando qualcosa come:

while (!condition) Thread.Sleep(10);

che è sicuramente più efficiente dello spinning puro, anche se esteticamente non è il massimo…

Chi preferire?

Lo spinning può essere migliore del blocking quando mi aspetto che una condizione sia verificata subito (nell’ordine di microsecondi) in quando evito l’overhead e la latenza del context switch necessario ai metodi blocking.

In tutti gli altri il blocking è da preferire.