sync.Mutex, RWMutex y WaitGroup
El paquete sync proporciona mutexes para el estado compartido y WaitGroup para esperar lotes de goroutines.
Busca en todas las páginas de la documentación
El paquete sync proporciona mutexes para el estado compartido y WaitGroup para esperar lotes de goroutines.
Úsalos cuando los pipelines de canales oscurezcan una sección crítica simple o una barrera de finalización.
sync.Mutex serializa el acceso de lectura/escritura a datos compartidos.
sync.RWMutex permite muchos lectores concurrentes pero escritores exclusivos.
sync.WaitGroup cuenta las goroutines pendientes; Wait se bloquea hasta que el contador llega a cero.
Los mutexes no deben copiarse después del primer uso; WaitGroup requiere pares Add/Done coincidentes.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
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()Cuándo usar esto:
RWMutex cuando las lecturas dominan y las escrituras son raras.WaitGroup en límites de bifurcación/unión sin pasar un canal de finalización (done channel).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())
}Lo que esto demuestra:
Mutex protege las escrituras en Inc.RWMutex el lock de lectura en Value permite lectores concurrentes.WaitGroup espera a todas las goroutines de incremento.Mutex.Lock se bloquea hasta que está disponible; Unlock libera.RWMutex.RLock se bloquea solo si un escritor tiene el lock; los escritores bloquean a todos los lectores.WaitGroup se incrementa con Add, se decrementa con Done, Wait se bloquea en cero.Mutex o WaitGroup duplica el estado interno - usa punteros o incrusta en structs pasadas por puntero.| Primitiva | Lectores | Escritores | Sobrecarga |
|---|---|---|---|
Mutex | Exclusivo | Exclusivo | Menor |
RWMutex | Concurrente | Exclusivo | Mayor escalabilidad de lectores |
| Llamada | Contrato |
|---|---|
Add(n) | Antes de iniciar goroutines (o dentro de la goroutine antes del trabajo) |
Done() | Una vez por cada Add(1) - idiomático defer wg.Done() |
Wait() | Después de todas las llamadas Add; se bloquea hasta que el contador sea cero |
// Incorrecto: copiar WaitGroup
wg2 := wg // roto
// Incorrecto: Add después de que Wait retorna
go func() { wg.Add(1) }() // carrera con Wait
// Correcto: Add antes de go
wg.Add(1)
go func() {
defer wg.Done()
}()Unlock Olvidado - Deadlock. Solución: defer mu.Unlock() inmediatamente después de Lock.WaitGroup.Add después de Wait - Carrera. Solución: todas las Add antes de Wait, o usar un canal de barrera.RWMutex para cargas de trabajo con muchas escrituras - Los lectores aún bloquean a los escritores; puede ser más lento que Mutex. Solución: hacer benchmark; a menudo Mutex simple gana.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
sync.Mutex | Struct compartida general | Con muchas lecturas y pocas escrituras (prueba RWMutex) |
sync/atomic | Contadores/flags simples | Invariantes complejas que requieren múltiples campos |
Pase de canal (Channel handoff) | Transferencia de propiedad | Caché caliente compartida con muchos lectores |
errgroup | Tareas + primer error | Unión simple sin errores |
Mutex + int está bien para contadores en memoria.
Los canales añaden sobrecarga a menos que el contador sea parte de una etapa de pipeline.
Sí - defer c.mu.RUnlock() después de RLock refleja los locks de escritura.
No - los locks de lectura tienen un costo de gestión.
Haz benchmark de tu patrón de acceso; el código con muchas escrituras puede preferir Mutex.
Wait entra en pánico si Add hace que el contador sea negativo.
Empareja cada Add(1) con exactamente un Done.
Sí, después de que Wait retorne y ninguna goroutine siga llamando a Done.
Espera a que Wait se complete antes de un nuevo lote de Add.
Los mutexes de Go no son estrictamente FIFO justos, pero evitan la inanición en la práctica.
No confíes en el orden de los locks para la corrección.
Incrusta mu sync.Mutex no exportado en tipos que controles.
Los mutexes exportados tientan a los llamadores a hacer lock incorrectamente.
El detector de carreras verifica que el lock cubra accesos conflictivos.
Los locks faltantes aún reportan carreras, incluso si las pruebas pasan a veces.
Los métodos que hacen lock deben usar receptores de puntero para que todos los llamadores compartan un único mutex.
WaitGroup es más simple para unir N trabajadores.
Los canales son excelentes para enviar resultados o señales de apagado también.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (GC por defecto Green Tea, go fix modernizers - verificar parche en build), chi (última - verificar en build), gin (última - verificar en build), echo (última - verificar en build), google.golang.org/grpc (última - verificar en build), sigs.k8s.io/controller-runtime (última - verificar en build), kubebuilder (última - verificar en build), tinygo (última - verificar objetivos de placa en build), wazero (última - verificar en build), y golangci-lint (última - verificar conjunto de linters en build).
Revisado por Chris St. John·Última actualización: 18 jul 2026