sync.Mutex, RWMutex & WaitGroup
O pacote sync fornece mutexes para estado compartilhado e WaitGroup para esperar por lotes de goroutines.
Busque em todas as páginas da documentação
O pacote sync fornece mutexes para estado compartilhado e WaitGroup para esperar por lotes de goroutines.
Use-os quando pipelines de canal obscurecerem uma seção crítica simples ou uma barreira de conclusão.
sync.Mutex serializa o acesso de leitura/escrita a dados compartilhados.
sync.RWMutex permite muitos leitores concorrentes, mas escritores exclusivos.
sync.WaitGroup conta as goroutines pendentes; Wait bloqueia até que a contagem chegue a zero.
Mutexes não devem ser copiados após o primeiro uso; WaitGroup requer pares Add/Done correspondentes.
Cartão de referência rápida - pronto para copiar e colar.
var (
mu sync.Mutex
cache map[string]string
wg sync.WaitGroup
)
func load(key string) {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
cache[key] = fetch(key)
}()
}
wg.Wait()Quando usar isso:
RWMutex quando leituras dominam e escritas são raras.WaitGroup em limites de fork/join sem passar um canal de término.package main
import (
"fmt"
"sync"
"time"
)
type SafeCounter struct {
mu sync.RWMutex
n int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
c.n++
c.mu.Unlock()
}
func (c *SafeCounter) Value() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.n
}
func main() {
var c SafeCounter
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 100; j++ {
c.Inc()
time.Sleep(time.Microsecond)
}
}()
}
wg.Wait()
fmt.Println(c.Value())
}O que isso demonstra:
Mutex protege escritas em Inc.RWMutex em Value permite leitores concorrentes.WaitGroup espera por todas as goroutines de incremento.Mutex.Lock bloqueia até ficar disponível; Unlock libera.RWMutex.RLock bloqueia apenas se um escritor detiver o bloqueio; escritores bloqueiam todos os leitores.WaitGroup incrementa com Add, decrementa com Done, Wait bloqueia em zero.Mutex ou WaitGroup duplica o estado interno - use ponteiros ou incorpore em structs passadas por ponteiro.| Primitiva | Leitores | Escritores | Sobrecarga |
|---|---|---|---|
Mutex | Exclusivo | Exclusivo | Menor |
RWMutex | Concorrente | Exclusivo | Maior escalabilidade de leitores |
| Chamada | Contrato |
|---|---|
Add(n) | Antes de iniciar goroutines (ou dentro da goroutine antes do trabalho) |
Done() | Uma vez por Add(1) - defer wg.Done() idiomático |
Wait() | Após todas as chamadas Add; bloqueia até o contador zerar |
// Errado: copiar WaitGroup
wg2 := wg // quebrado
// Errado: Add após Wait retornar
go func() { wg.Add(1) }() // race com Wait
// Certo: Add antes de go
wg.Add(1)
go func() {
defer wg.Done()
}()Unlock esquecido - Deadlock. Correção: defer mu.Unlock() imediatamente após Lock.WaitGroup.Add após Wait - Race. Correção: todos os Add antes de Wait, ou use um canal de barreira.Mutex. Correção: benchmark; muitas vezes o Mutex simples vence.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
sync.Mutex | Struct compartilhada geral | Com muitas leituras e poucas escritas (tente RWMutex) |
sync/atomic | Contadores/flags simples | Invariantes complexas que necessitam de múltiplos campos |
| Handoff de canal | Transferência de propriedade | Cache quente compartilhado com muitos leitores |
errgroup | Tarefas + primeiro erro | Junção simples sem erros |
Mutex + int é bom para contadores em memória.
Canais adicionam sobrecarga, a menos que o contador seja parte de um estágio de pipeline.
Sim - defer c.mu.RUnlock() após RLock espelha os bloqueios de escrita.
Não - bloqueios de leitor têm custo de contabilidade.
Faça benchmark do seu padrão de acesso; código com muitas escritas pode preferir Mutex.
Wait entra em pânico se Add tornar o contador negativo.
Combine cada Add(1) com exatamente um Done.
Sim, após Wait retornar e nenhuma goroutine ainda chamar Done.
Espere até que Wait complete antes de um novo lote de Add.
Mutexes Go não são estritamente FIFO justos, mas evitam inanição na prática.
Não confie na ordem de bloqueio para correção.
Incorpore mu sync.Mutex não exportado em tipos que você controla.
Mutexes exportados tentam os chamadores a bloquear incorretamente.
O detector de race verifica se o bloqueio cobre acessos conflitantes.
Bloqueios ausentes ainda relatam races, mesmo que os testes passem às vezes.
Métodos que bloqueiam devem usar receptores de ponteiro para que todos os chamadores compartilhem um único mutex.
WaitGroup é mais simples para juntar N workers.
Canais são excelentes para enviar resultados ou sinais de desligamento também.
Versões de Stack: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verificar patch na compilação), chi (última - verificar na compilação), gin (última - verificar na compilação), echo (última - verificar na compilação), google.golang.org/grpc (última - verificar na compilação), sigs.k8s.io/controller-runtime (última - verificar na compilação), kubebuilder (última - verificar na compilação), tinygo (última - verificar alvos de placa na compilação), wazero (última - verificar na compilação), e golangci-lint (última - verificar conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026