En Go, el acceso concurrente a estructuras de datos puede causar condiciones de carrera, lo que genera comportamientos inesperados. Los mapas concurrentes (concurrent maps) son una solución común para manejar de manera segura el acceso simultáneo a mapas desde múltiples goroutines. Aunque Go no tiene un tipo de mapa concurrente nativo, se puede lograr la concurrencia utilizando mecanismos como sync.Map o bloqueando el acceso a un mapa tradicional con sync.Mutex.
Un mapa concurrente es una estructura de datos que permite a múltiples goroutines acceder y modificar un mapa al mismo tiempo sin causar conflictos o condiciones de carrera. Go ofrece la biblioteca sync
para este propósito, donde puedes encontrar:
1. sync.Map: Un tipo de mapa diseñado específicamente para el acceso concurrente, simplificando su uso y proporcionando operaciones como Store
, Load
, y Delete
.
2. sync.Mutex: Un mecanismo para bloquear manualmente el acceso a un mapa tradicional y garantizar que solo una goroutine pueda acceder al mapa a la vez.
sync.Map
sync.Map
es una estructura de datos especial en Go que facilita el manejo de mapas concurrentes sin necesidad de bloquear manualmente.
sync.Map
:
package main
import (
"fmt"
"sync"
)
func main() {
var concurrentMap sync.Map
// Almacenar valores en el mapa
concurrentMap.Store("clave1", "valor1")
concurrentMap.Store("clave2", "valor2")
// Cargar valores del mapa
valor, ok := concurrentMap.Load("clave1")
if ok {
fmt.Println("Clave1:", valor)
}
// Eliminar un valor del mapa
concurrentMap.Delete("clave2")
// Iterar sobre los elementos del mapa
concurrentMap.Range(func(key, value interface{}) bool {
fmt.Println(key, ":", value)
return true
})
}
En este ejemplo:
Store
: Inserta o actualiza un valor en el mapa.
Load
: Recupera un valor almacenado en el mapa.
Delete
: Elimina un elemento del mapa.
Range
: Permite iterar de manera concurrente sobre todos los elementos del mapa.
sync.Mutex
con un mapa tradicional
Otra manera de hacer un mapa concurrente en Go es usar un mapa tradicional y proteger el acceso a él utilizando un mutex.
sync.Mutex
:
package main
import (
"fmt"
"sync"
)
type SafeMap struct {
m map[string]string
mux sync.Mutex
}
func (sm *SafeMap) Store(key, value string) {
sm.mux.Lock()
sm.m[key] = value
sm.mux.Unlock()
}
func (sm *SafeMap) Load(key string) (string, bool) {
sm.mux.Lock()
defer sm.mux.Unlock()
value, ok := sm.m[key]
return value, ok
}
func main() {
safeMap := SafeMap{m: make(map[string]string)}
// Almacenar valores
safeMap.Store("clave1", "valor1")
safeMap.Store("clave2", "valor2")
// Cargar y mostrar un valor
if valor, ok := safeMap.Load("clave1"); ok {
fmt.Println("Clave1:", valor)
}
}
En este ejemplo, el mutex garantiza que solo una goroutine pueda modificar o leer el mapa a la vez, evitando condiciones de carrera.
sync.Map
o sync.Mutex
?
sync.Map
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var concurrentMap sync.Map
var wg sync.WaitGroup
// Iniciar 5 goroutines que escriben al mapa
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
concurrentMap.Store(i, fmt.Sprintf("valor%d", i))
}(i)
}
// Esperar que todas las goroutines terminen
wg.Wait()
// Iterar sobre los elementos del mapa
concurrentMap.Range(func(key, value interface{}) bool {
fmt.Println(key, ":", value)
return true
})
}
Este ejemplo inicia 5 goroutines que almacenan valores en el mapa de manera concurrente, utilizando sync.Map
para evitar problemas de concurrencia.
Para más detalles sobre sync.Map
y mecanismos de sincronización en Go, visita la documentación oficial de Go.
Jorge García
Fullstack developer