Estado compartilhado entre goroutines quebra calado. Roda no modo sem proteção e olha o contador parar num número errado por causa da corrida. Depois liga o mutex e o channel: os dois consertam, cada um do seu jeito, e você vê o que isso custa em contenção.
Escolhe o modo e clica em Rodar. Cada goroutine faz 4 incrementos; o esperado é goroutines × 4.
na seção críticaesperando o lockcorrida (lendo junto)terminou
Incrementar um contador é read-modify-write: lê, soma 1, grava. Se duas goroutines leem o mesmo valor antes de qualquer uma gravar, uma escrita sobrescreve a outra e um incremento some. O resultado fica menor que o esperado, de forma não-determinística. A regra: mutex pra proteger estado, channel pra comunicar.
Ferramenta
Use para
sync.Mutex
proteger estado / seção crítica (contador, map, cache)
sync.RWMutex
muitos leitores, poucos escritores
channel
transferir posse, coordenar, pipelines
sync/atomic
contador simples sem lock (mais rápido)
Mutex: proteger o estado
type Counter struct {
mu sync.Mutex
n int
}
func (c *Counter) Inc() {
c.mu.Lock()
c.n++ // seção crítica: curta
c.mu.Unlock()
}
RWMutex: muitos leitores
mu.RLock() // leituras em paralelo
_ = cache[key]
mu.RUnlock()
mu.Lock() // escritor exclusivo
cache[key] = val
mu.Unlock()
Channel: uma goroutine dona
ch := make(chanint)
gofunc() { // só ela toca em n
n := 0for d := range ch {
n += d
}
}()
ch <- 1// cada goroutine só envia
🧠 Desafio — Mutex vs Channels
Roda os três modos aqui de cima antes de responder. As duas últimas são de reflexão: escreve a sua e só então revela o modelo.