Camada Zero · 09 · Race Conditions

Você roda o programa e dá 2000. Roda de novo, 1998. De novo, 1995. counter++ parece uma operação só, mas são três passos, e duas goroutines pisam no resultado uma da outra. Aqui você vê o incremento sumir.
counter compartilhado = 0
16Esperado
0Real
0Updates perdidos
counter++ não é atômico. O processador faz READ counter, soma 1, WRITE counter de volta. Entre o READ e o WRITE de uma goroutine, a outra pode ter escrito, e o WRITE atrasado sobrescreve esse trabalho. Resultado: incrementos somem e o total fica abaixo do esperado.
AbordagemCustoQuando usar
counter++ sem lockquebranunca sob concorrência
atomic.AddInt641 instrução de CPUcontador ou flag simples
sync.Mutexlock / unlockoperação composta (struct, mapa)

O bug

var counter int

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                counter++ // READ, +1, WRITE: 3 passos
            }
        }()
    }
    wg.Wait()
    fmt.Println(counter) // quase nunca 2000
}
// flagra com: go run -race main.go

As correções

// atomic: uma instrução, ideal pra contador
var counter int64
atomic.AddInt64(&counter, 1)

// mutex: pra operação composta
var mu sync.Mutex
mu.Lock()
counter++
mu.Unlock()

🧠 Desafio — Race Conditions

Roda o visualizador nos três modos antes de responder. As duas últimas são de reflexão: escreve a sua e só então revela o modelo.

🔧 Prática — ache o bug

Esse código deveria contar até 2000, mas quase nunca conta. Clica na linha que causa o bug.