Cos'è un mutex, quali tipi di mutex esistono e come usarli?
sobes.tech AI
Risposta dell'AI
Il mutex (dall'inglese MUTual EXclusion) è una primitive di sincronizzazione utilizzata per garantire l'accesso esclusivo a una risorsa condivisa tra goroutine concorrenti. Garantisce che in ogni momento solo una goroutine possa possedere il mutex e accedere ai dati protetti.
In Go, il pacchetto sync fornisce due principali tipi di mutex:
-
sync.Mutex: Mutex standard. Fornisce i metodiLock()eUnlock(). Se il mutex è già bloccato, la chiamata aLock()blocca la goroutine corrente fino a quando non viene rilasciato.Unlock()sblocca il mutex.var mu sync.Mutex var counter int func increment() { mu.Lock() // Blocca l'accesso a counter counter++ mu.Unlock() // Sblocca l'accesso } -
sync.RWMutex: Mutex per lettura/scrittura. Permette a molte goroutine di leggere contemporaneamente la risorsa protetta, ma solo a una di scrivere. Fornisce i metodiLock(),Unlock()(per scrittura),RLock()eRUnlock()(per lettura).var rwMu sync.RWMutex var data []int func readData() { rwMu.RLock() // Blocca per la lettura // Legge data fmt.Println(data) rwMu.RUnlock() // Sblocca la lettura } func writeData(val int) { rwMu.Lock() // Blocca per la scrittura // Scrive in data data = append(data, val) rwMu.Unlock() // Sblocca la scrittura }
L'uso dei mutex è necessario per prevenire condizioni di gara (race conditions), quando più goroutine tentano di modificare contemporaneamente gli stessi dati, portando a risultati imprevedibili.
Regole di utilizzo:
- Chiamare
Lock()prima di accedere alla risorsa protetta. - Chiamare
Unlock()dopo aver terminato l'accesso. Si consiglia di usaredefer mu.Unlock()per garantire il rilascio anche in caso di panico. - Non tentare di bloccare un mutex già bloccato dalla stessa goroutine (può portare a deadlock).
- Non tentare di sbloccare un mutex non bloccato.
- Quando si usa
RWMutex, usareRLock/RUnlockper le operazioni di lettura eLock/Unlockper le operazioni di scrittura. - I mutex non devono essere copiati dopo il primo utilizzo. Passarli per riferimento o puntatore.
Esempio di uso di defer:
func safeIncrement() {
mu.Lock()
defer mu.Unlock() // Garantisce il rilascio
counter++
}