I miei due centesimi su SpinLock in .Net

Immagina una situazione in cui un thread sta tentando di acquisire l'accesso a una risorsa condivisa ma la risorsa è già bloccata, quindi il thread deve attendere fino al rilascio del blocco. È qui che entra in gioco la sincronizzazione dei thread. La sincronizzazione dei thread viene utilizzata per impedire a più thread di accedere contemporaneamente a una risorsa condivisa. Microsoft .Net Framework fornisce il supporto per una gamma di primitive di sincronizzazione che possono essere utilizzate per controllare il comportamento dei thread ed evitare le race condition. Mutex e Spinlock sono due popolari meccanismi di sincronizzazione utilizzati per sincronizzare l'accesso a una risorsa condivisa.

Uno SpinLock è un'alternativa al blocco della sincronizzazione. SpinLock (noto anche come "Busy Waiting") è un meccanismo che può essere utilizzato per fare in modo che un thread che cerca di acquisire un blocco attenda in un ciclo finché non può accedere alla risorsa. Nota che SpinLock può funzionare più velocemente rispetto a Mutex poiché il cambio di contesto è ridotto. Tuttavia, è necessario utilizzare SpinLock solo se si suppone che la sezione critica esegua una quantità minima di lavoro, ovvero, SpinLock viene mantenuto per un periodo di tempo molto breve. Gli SpinLock sono generalmente preferiti nei sistemi multiprocessore simmetrici per eseguire il polling costante della disponibilità di una risorsa al posto dei cambi di contesto.

Cos'è SpinLock e perché è necessario?

SpinLock esegue un'attesa intensa e può offrire prestazioni migliori se utilizzato in sistemi multi-core, specialmente quando è economico aspettare in un ciclo e raggruppare una risorsa piuttosto che bloccarla. Ciò è particolarmente utile quando i tempi di blocco del blocco sono di breve durata. In altre parole, è possibile sfruttare SpinLock nei sistemi multi-core per ridurre l'overhead coinvolto nel cambio di contesto se il tempo da trascorrere all'interno della sezione critica è ridotto. Una sezione critica può essere definita come una struttura dati o una risorsa condivisa da più thread ma uno e solo un thread può avere accesso ad essa in un dato momento.

Va notato che mantenere SpinLock per una durata maggiore sarebbe semplicemente uno spreco di risorse del sistema e dannoso per le prestazioni dell'applicazione. In sostanza, se ti aspetti che il blocco abbia una durata significativa, SpinLock non dovrebbe mai essere usato: usa SpinLock solo quando i tempi di blocco del blocco sono di una durata ragionevolmente ridotta.

Gli SpinLock vengono in genere utilizzati quando si lavora con gli interrupt per eseguire un'attesa impegnata all'interno di un ciclo fino a quando la risorsa non viene resa disponibile. SpinLock non fa sì che il thread venga interrotto, anzi, continua a girare finché non viene rilasciato il blocco sulla risorsa.

Programmazione SpinLock in .Net

Notare che uno SpinLock è definito come una struttura in .Net, cioè è definito come un tipo di valore per motivi di prestazioni. Quindi, se stai passando un'istanza SpinLock, dovresti passarla per riferimento e non per valore. In questa sezione esploreremo come programmare SpinLock in .Net. Per implementare SpinLock in .Net, è necessario sfruttare la classe SpinLock disponibile nello spazio dei nomi System.Threading.

Il seguente listato di codice mostra come usare SpinLock in .Net.

SpinLock spinLock = new SpinLock (true);

bool isLocked = false;

try

{

spinLock.Enter (ref isLocked);

// Write your usual code here

}

finally

{

if (isLocked)

   spinLock.Exit();

}

SpinWait

Nota che come SpinLock, SpinWait è anche una struttura e non una classe. Simile a SpinLock, è possibile utilizzare SpinWait per scrivere codice di sincronizzazione senza blocco che può "girare" anziché bloccare. SpinWait può essere usato per ridurre il consumo di risorse eseguendo una rotazione intensiva della CPU per 10 iterazioni dopo che fornirà il controllo chiamando Thread.Yield e Thread.Sleep. In altre parole, SpinWait può essere utilizzato per limitare la rotazione intensiva della CPU a un numero fisso di iterazioni. L'MSDN afferma: "System.Threading.SpinWait è un tipo di sincronizzazione leggero che è possibile utilizzare in scenari di basso livello per evitare i costosi cambi di contesto e le transizioni del kernel necessarie per gli eventi del kernel."

Per utilizzare SpinWait nel codice, è possibile sfruttare il metodo statico SpinUntil () della struttura SpinWait oppure sfruttare il metodo non statico SpinOnce (). Il frammento di codice seguente illustra come utilizzare SpinWait.

SpinWait spinWait = new SpinWait();

bool shouldSpin;

while (!shouldSpin)

{

   Thread.MemoryBarrier(); spinWait.SpinOnce();

}