Sobes.tech
Middle

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 metodi Lock() e Unlock(). Se il mutex è già bloccato, la chiamata a Lock() 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 metodi Lock(), Unlock() (per scrittura), RLock() e RUnlock() (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:

  1. Chiamare Lock() prima di accedere alla risorsa protetta.
  2. Chiamare Unlock() dopo aver terminato l'accesso. Si consiglia di usare defer mu.Unlock() per garantire il rilascio anche in caso di panico.
  3. Non tentare di bloccare un mutex già bloccato dalla stessa goroutine (può portare a deadlock).
  4. Non tentare di sbloccare un mutex non bloccato.
  5. Quando si usa RWMutex, usare RLock/RUnlock per le operazioni di lettura e Lock/Unlock per le operazioni di scrittura.
  6. 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++
}