Sobes.tech
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. Usar defer mu.Unlock() inmediatamente después de mu.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 a Unlock() en un mutex no bloqueado causará un pánico en tiempo de ejecución.
  • No copiar mutexes: La estructura sync.Mutex no 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.RWMutex para lectura/escritura: Si la mayoría de las operaciones son lectura y las escrituras son raras, sync.RWMutex puede 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 -race al 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]
}