Manejo de Condiciones de Carrera en Go: Implementación de Mutex
Clase 6 de 19 • Curso de Go Avanzado: Concurrencia y Patrones de Diseño
Contenido del curso
Concurrencia
- 2

Condiciones de Carrera en Programación Concurrente con Go
04:52 min - 3

Evitar Condiciones de Carrera en Go con Mutex y WaitGroup
11:26 min - 4

Lectura y Escritura Concurrente en Go con RWMutex
06:15 min - 5

Creación de un Sistema de Caché Concurrente en Go
14:29 min - 6

Manejo de Condiciones de Carrera en Go: Implementación de Mutex
Viendo ahora - 7

Cache concurrente en Go para cálculos intensivos de Fibonacci
16:31 min
Patrones de diseño
- 8

Patrones de Diseño en Programación Orientada a Objetos con Go
04:53 min - 9

Patrón de Diseño Factory en Go: Creación y Uso Práctico
15:17 min - 10

Implementación de Singleton en Go para Conexiones de Base de Datos
08:48 min - 11

Patrón de Diseño Adapter en Go: Implementación Práctica
09:51 min - 12

Patrón de Diseño Observer en Go: Implementación Práctica
11:59 min - 13

Implementación del Patrón de Diseño Strategy en Go
08:53 min
Net
- 14

Escaneo de Puertos TCP con Go: Introducción Básica
07:45 min - 15

Concurrencia en Go: Escáner de Puertos Eficiente y Personalizable
09:16 min - 16

Implementación de NetCAD como Cliente TCP en Go
10:14 min - 17

Construcción de Back End para Servidor de Chat en Go
16:08 min - 18

Implementación de un Servidor de Chat en Go con TCP
13:54 min
Conclusión
¿Cómo crear un sistema de caché concurrente en Go?
La eficiencia en el cálculo de la serie de Fibonacci demuestra los beneficios de un sistema de caché bien diseñado. Sin embargo, cuando introducimos concurrencia, la ejecución puede llevar a condiciones de carrera que afectan la consistencia y fiabilidad del programa. Aquí exploraremos cómo abordar estos desafíos y optimizar un sistema de caché concurrente usando la robusta capacidad de concurrencia de Go.
¿Qué cambios hicimos en nuestra función principal para manejar concurrencia?
Para manipular concurrente las funciones de nuestro sistema de caché, debimos realizar ajustes en la función main:
-
Creación de WaitGroup: Este grupo nos permite sincronizar las goroutines y asegurarnos de que todas se completen antes de finalizar el programa.
-
Uso de Funciones Anónimas: Introducimos funciones anónimas junto con la palabra clave
gopara ejecutar las operaciones en nuevas goroutines. Este enfoque permite que cada cálculo de Fibonacci se realice de manera concurrente. -
Manejo de Contadores: Se utilizó
wg.Add(1)para incrementar el contador al iniciar una goroutine ywg.Done()para decrementar al finalizar, garantizando la correcta espera del programa principal mediantewg.Wait().
var wg sync.WaitGroup
for i, n := range numbers {
wg.Add(1)
go func(index, num int) {
defer wg.Done()
calcularFibonacci(num)
}(i, n)
}
wg.Wait()
¿Cómo resolvimos las condiciones de carrera con logs?
Las condiciones de carrera pueden manifestarse cuando varias goroutines acceden a recursos compartidos de forma desincronizada. Para mitigar este problema, implementamos sync.Mutex en nuestro código:
- Sincronización con Mutex: Antes de acceder a datos compartidos, usamos un lock para asegurar la exclusividad y desbloqueamos justo antes de retornar los resultados. Esto evita que múltiples goroutines modifiquen datos simultáneamente, lo que podría llevar a inconsistencias.
memory := struct{
cache map[int]int
lock sync.Mutex
}{
cache: make(map[int]int),
}
func (m *memory) Get(key int) (int, bool) {
m.lock.Lock()
defer m.lock.Unlock()
val, exists := m.cache[key]
return val, exists
}
- Optimización del uso de locks: Inicialmente, bloqueamos todo el bloque de funcionalidad. Sin embargo, al mejorar, bloqueamos sólo cuando es necesario. Movimos el lock para después de verificar la existencia del valor, reduciendo así el impacto negativo en el rendimiento.
¿Cómo mejoramos el rendimiento del sistema?
Después de establecer los locks, notamos un decremento en el rendimiento debido al bloqueo excesivo:
-
Reanching los locks: Ajustamos los lugares donde se aplicaban los locks, aumentando la eficiencia. Al hacerlo, evitamos bloquear completamente el programa durante operaciones que no lo requieren.
-
Pruebas de carrera: Usamos la herramienta
-racede Go para verificar que no se presenten nuevas condiciones de carrera, garantizando así la correcta implementación de nuestras mejoras.
go run -race main.go
Este enfoque resultó en un sistema más eficiente que previene el recalculo innecesario de datos y optimiza el uso de memoria.
Con estos cambios, hemos creado un sistema de caché concurrente robusto que utiliza únicamente las librerías estándar de Go. Este sistema, que ahora maneja la concurrencia de manera efectiva, permite almacenar resultados de cálculos previos para evitar cálculos repetitivos y mejorar el rendimiento significativamente. ¡Continúa explorando los vastos horizontes de la programación concurrente en Go!