Lo struct SpinWait
permette di mettere in pausa un Thread utilizzando lo spinning, quindi facendogli fare del lavoro inutile in loop.
Effettuando un wait con spinning usando un classico while
while (!condition);
puΓ² portare a numerosi problemi:
- Il thread consumerΓ tutti i core della CPU fino a che
condition
non sarΓtrue
; - Gli altri Thread dellβapplicazione rallenteranno (starvation);
- Perfino il Thread che dovrΓ mettere
condition
atrue
rallenterΓ , portando ad un loop di rallentamento orribile (chiamato priority inversion); - In caso di CPU single core la priority inversion Γ¨ pressochΓ© certa.
La struct SpinWait
risolve questi problemi in due modi:
- Limita il numero di spinning CPU intensive ad un massimo numero di iterazioni: dopo aver raggiunto tale numero massimo fa lo yield del time slice a lui assegnato utilizzando
Thread.Sleep
oThread.Yield
(ricordo che il thread scheduler del sistema operativo assegna dei time slice ad ogni thread e rapidamente continua a cambiare lβesecuzione, aumento inoltre il parallelismo in base al numero di CPU a disposizione); - Rileva se sta lavorando su una CPU single core e in quel caso fa lo
yield
ad ogni ciclo.
SpinWait
si puΓ² utilizzare con la sua classe statica in questo modo:
SpinWait.SpinUntil(() => myPredicate(), 1000)
che di fatto aspetta che myPredicate()
diventi true per al massimo 1000 ms.
Aspettare facendo spinning per 1000ms non ha senso, potrebbe essere una idea piΓΉ interessante provare lo spinning per, esempio, 10ms e successivamente un Thread.sleep
classico.
Una alternativa Γ¨ utilizzare il metodo SpinOnce()
allβinterno di un while
che esce su una determinata condition
var sw = new SpinWait();
while (!condition)
{
sw.SpinOnce();
}
Oppure Γ¨ possibile utilizzare la property NextSpinWillYield
per poter essere intenzionale nel block:
private void SpinBeforeBlocking()
{
var wait = new SpinWait();
while (!_condition)
{
// Utilizzando questa property posso sapere se sono arrivato al numero massimo di spin disponibili e il prossimo spin farΓ uno yielding
if (wait.NextSpinWillYield)
{
/* block! */
}
else
{
wait.SpinOnce();
}
}
}
Eβ estremamente interessante e ben commentato il codice ufficiale di Microsoft, quindi consiglio di darci una letta.