Middle
O que é importante lembrar ao trabalhar com mutexes em Go?
sobes.tech IA
Resposta da IA
Ao trabalhar com mutexes (sync.Mutex) em Go, é importante lembrar o seguinte:
- Capturar e libertar: É necessário adquirir (
Lock()) o mutex antes de aceder aos dados partilhados e libertá-lo (Unlock()) após terminar com eles. Usardefer mu.Unlock()imediatamente apósmu.Lock()é uma boa prática para evitar esquecimentos. - Exclusividade: O mutex fornece acesso exclusivo a um recurso a um único goroutine de cada vez.
- Estado "bloqueado/desbloqueado": Chamar
Lock()num mutex já bloqueado fará com que o goroutine fique bloqueado até que outro o liberte. ChamarUnlock()num mutex não bloqueado causará um pânico em tempo de execução. - Não copiar mutexes: A estrutura
sync.Mutexnão foi concebida para ser copiada após o seu primeiro uso. Passar um mutex por valor ou copiar a estrutura que o contém pode levar a uma sincronização incorreta e a panics. - Deadlocks (bloqueios mútuos): Ocorrem quando duas ou mais goroutines esperam que a outra libere recursos. Exemplo típico: a goroutine A bloqueia R1 e espera R2, enquanto que a goroutine B bloqueia R2 e espera R1.
- Livelocks (bloqueios vivos): Situação onde as goroutines mudam constantemente de estado em resposta às ações umas das outras, sem realizar trabalho útil nem avançar. É menos comum do que deadlock.
- Fome (starvation): Situação onde uma ou várias goroutines não conseguem aceder a um recurso partilhado porque este está constantemente bloqueado por outras. Nos mutexes do Go, a fome não é garantida.
- Uso de
sync.RWMutexpara leitura/escrita: Se a maioria das operações for leitura e as escritas forem raras,sync.RWMutexpode ser mais eficiente. Permite múltiplas leituras simultâneas, mas bloqueia durante uma escrita. - Bloqueios curtos: Tente manter os bloqueios o mais curtos possível. Quanto mais tempo um mutex estiver bloqueado, mais goroutines esperarão, reduzindo o paralelismo.
- Testar condições de corrida: Usar a flag
-raceao compilar e executar testes (go test -race) ajuda a detectar condições de corrida, muitas vezes causadas por uso incorreto de mutexes.
Exemplo de uso de defer:
import (
"sync"
)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
// Inc incrementa de forma segura o contador para uma chave dada.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Liberamos ao sair da função.
defer c.mu.Unlock()
c.v[key]++
}
// Value devolve o valor atual do contador para uma chave dada.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Libertamos após obter o valor.
defer c.mu.Unlock()
return c.v[key]
}