Evitar Condiciones de Carrera en Go con Mutex y WaitGroup

Clase 3 de 19Curso de Go Avanzado: Concurrencia y Patrones de Diseño

Resumen

¿Cómo podemos manejar la condición de carrera en Go?

La programación concurrente en Go nos permite realizar múltiples tareas a la vez, incrementando la eficiencia de nuestros programas. Sin embargo, esto también puede llevar a problemas si no se manejan adecuadamente los accesos compartidos a las variables. Uno de estos problemas es la condición de carrera, un fenómeno donde múltiples rutinas acceden y manipulan datos compartidos al mismo tiempo, causando comportamientos incorrectos o inconsistentes.

¿Cómo podemos identificar una condición de carrera?

Para empezar, Go ofrece una herramienta para detectar condiciones de carrera. Podemos utilizar la bandera --race al compilar nuestro programa. Esta simple acción nos dará un aviso si el compilador detecta subrutinas accediendo indeciblemente a variables compartidas. La detección temprana es crucial para evitar problemas en producción.

go run --race main.go

¿Cómo podemos evitar una condición de carrera en Go?

Manejando adecuadamente las condiciones de carrera, podemos asegurar la integridad de los datos. Un enfoque común es utilizar locks o candados. En Go, podemos utilizar la estructura sync.Mutex que actúa como un candado que regula el acceso a las variables compartidas.

Implementación básica de locks en Go

  1. Definir un Mutex: Primero, definimos un sync.Mutex en nuestro programa, que usaremos para controlar el acceso.
var mu sync.Mutex
  1. Bloquear y desbloquear el acceso: Dentro de las funciones que modifican los datos compartidos, utilizamos los métodos Lock y Unlock de Mutex para bloquear y desbloquear el acceso a las variables.
func depositar(amount int) {
    mu.Lock()
    b := balance
    balance = b + amount
    mu.Unlock()
}

Este código garantiza que solo una go rutina pueda modificar el balance a la vez, evitando por ende la condición de carrera.

¿Qué ocurre si solo realizamos lecturas?

Aunque el uso de sync.Mutex soluciona los problemas de concurrencia en operaciones de escritura, puede resultar ineficiente para operaciones que solo requieren lectura. En estos casos, donde no estamos modificando los datos, hay otro tipo de lock en Go más apropiado llamado sync.RWMutex. Este permite múltiples lecturas concurrentes pero restringe el acceso total al realizar una escritura. Lo exploraremos más en profundidad en las siguientes lecciones.