Middle
¿Qué se debe recordar al trabajar con mutex en Go?
sobes.tech AI
Respuesta de la IA
Al trabajar con mutexes (sync.Mutex) en Go, es importante recordar lo siguiente:
- Capturar y liberar: Es necesario adquirir (
Lock()) el mutex antes de acceder a los datos compartidos y liberarlo (Unlock()) después de terminar con ellos. Usardefer mu.Unlock()inmediatamente después demu.Lock()es una buena práctica para evitar olvidos. - Exclusividad: El mutex proporciona acceso exclusivo a un recurso a un solo goroutine a la vez.
- Estado "bloqueado/desbloqueado": Llamar a
Lock()en un mutex ya bloqueado provocará que el goroutine se bloquee hasta que otro lo libere. Llamar aUnlock()en un mutex no bloqueado causará un pánico en tiempo de ejecución. - No copiar mutexes: La estructura
sync.Mutexno está diseñada para ser copiada después del primer uso. Pasar un mutex por valor o copiar la estructura que lo contiene puede causar sincronización incorrecta y pánicos. - Deadlocks (bloqueos mutuos): Ocurren cuando dos o más goroutines esperan que la otra libere recursos. Ejemplo típico: la goroutine A bloquea R1 y espera R2, mientras que la goroutine B bloquea R2 y espera R1.
- Livelocks (bloqueos vivos): Situaciones donde las goroutines cambian constantemente de estado en respuesta a las acciones de otras, sin realizar trabajo útil ni avanzar. Es menos común que los deadlocks.
- Hambre (starvation): Cuando una o varias goroutines no pueden acceder a un recurso compartido debido a que otras lo mantienen constantemente. En los mutexes de Go, la hambruna no está garantizada.
- Uso de
sync.RWMutexpara lectura/escritura: Si la mayoría de las operaciones son lectura y las escrituras son raras,sync.RWMutexpuede ser más eficiente. Permite múltiples lecturas simultáneas, pero bloquea tanto lectura como escritura durante una escritura. - Bloqueos cortos: Mantén los bloqueos lo más breves posible. Cuanto más tiempo esté bloqueado un mutex, más goroutines esperarán, reduciendo el paralelismo.
- Pruebas de condiciones de carrera: Usar la bandera
-raceal compilar y ejecutar pruebas (go test -race) ayuda a detectar condiciones de carrera, a menudo causadas por un uso incorrecto de mutexes.
Ejemplo de uso de defer:
import (
"sync"
)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
// Inc incrementa de forma segura el contador para una clave dada.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Liberamos al salir de la función.
defer c.mu.Unlock()
c.v[key]++
}
// Value devuelve el valor actual del contador para una clave dada.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Liberamos después de obtener el valor.
defer c.mu.Unlock()
return c.v[key]
}