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.