Sobes.tech
Middle

Cosa bisogna ricordare quando si lavora con i mutex in Go?

sobes.tech AI

Risposta dell'AI

Lavorando con mutex (sync.Mutex) in Go, è importante ricordare quanto segue:

  • Acquisizione e rilascio: È necessario acquisire (Lock()) il mutex prima di accedere ai dati condivisi e rilasciarlo (Unlock()) dopo aver terminato. Usare defer mu.Unlock() immediatamente dopo mu.Lock() è una buona pratica per evitare di dimenticare di rilasciarlo.
  • Esclusività: Il mutex fornisce accesso esclusivo a una risorsa a un solo goroutine alla volta.
  • Stato "bloccato/sbloccato": Chiamare Lock() su un mutex già bloccato bloccherà il goroutine chiamante fino a quando non sarà rilasciato da un'altra goroutine. Chiamare Unlock() su un mutex non bloccato causerà un panic in fase di esecuzione.
  • Non copiare i mutex: La struttura sync.Mutex non è progettata per essere copiata dopo il suo primo utilizzo. Passare un mutex per valore o copiare la struttura che lo contiene può portare a sincronizzazione errata e panico.
  • Deadlock (stallo): Si verifica quando due o più goroutine aspettano che l'altra rilasci risorse. Scenario tipico: la goroutine A blocca R1 e aspetta R2, mentre la goroutine B blocca R2 e aspetta R1.
  • Livelock (stallo attivo): Situazione in cui le goroutine cambiano continuamente stato in risposta alle azioni degli altri, senza svolgere lavoro utile o avanzare. È meno comune del deadlock.
  • Fame (starvation): Situazione in cui una o più goroutine non riescono ad accedere a una risorsa condivisa perché è costantemente bloccata da altre. Nei mutex di Go, la fame non è garantita.
  • Utilizzo di sync.RWMutex per lettura/scrittura: Se la maggior parte delle operazioni sono di lettura e le scritture sono rare, sync.RWMutex può essere più efficiente. Permette a più goroutine di leggere contemporaneamente, ma blocca durante una scrittura.
  • Blocchi brevi: Cerca di mantenere i blocchi il più brevi possibile. Più a lungo un mutex è bloccato, più goroutine dovranno aspettare, riducendo il parallelismo.
  • Test di condizioni di gara: Usare il flag -race durante la compilazione e l'esecuzione dei test (go test -race) aiuta a rilevare condizioni di gara, spesso causate da un uso scorretto dei mutex.

Esempio di utilizzo di defer:

import (
	"sync"
)

type SafeCounter struct {
	mu sync.Mutex
	v  map[string]int
}

// Inc incrementa in modo sicuro il contatore per una chiave data.
func (c *SafeCounter) Inc(key string) {
	c.mu.Lock()
	// Rilasciamo al termine della funzione.
	defer c.mu.Unlock()
	c.v[key]++
}

// Value restituisce il valore corrente del contatore per una chiave.
func (c *SafeCounter) Value(key string) int {
	c.mu.Lock()
	// Rilasciamo dopo aver ottenuto il valore.
	defer c.mu.Unlock()
	return c.v[key]
}