Introducción
Este laboratorio tiene como objetivo demostrar cómo utilizar mutexes para acceder de manera segura a datos a través de múltiples goroutines.
Mutexes
El problema que se debe resolver en este laboratorio es incrementar un contador nombrado en un bucle utilizando múltiples goroutines y asegurarse de que el acceso al contador esté sincronizado.
- Utilice una estructura
Containerpara almacenar un mapa de contadores. - Utilice un
Mutexpara sincronizar el acceso al mapacounters. - La estructura
Containerdebe tener un métodoincque tome una cadenanamee incremente el contador correspondiente en el mapacounters. - El método
incdebe bloquear el mutex antes de acceder al mapacountersy desbloquearlo al final de la función utilizando una declaracióndefer. - Utilice la estructura
sync.WaitGrouppara esperar a que las goroutines terminen. - Utilice la función
fmt.Printlnpara imprimir el mapacounters.
## Ejecutar el programa muestra que los contadores
## se actualizan como se esperaba.
## A continuación, veremos cómo implementar esta misma tarea
## de administración de estado utilizando solo goroutines y canales.
A continuación se muestra el código completo:
// En el ejemplo anterior vimos cómo administrar un estado
// de contador simple utilizando [operaciones atómicas](atomic-counters).
// Para un estado más complejo, podemos utilizar un [_mutex_](https://en.wikipedia.org/wiki/Mutual_exclusion)
// para acceder de manera segura a datos a través de múltiples goroutines.
package main
import (
"fmt"
"sync"
)
// Container almacena un mapa de contadores; dado que queremos
// actualizarlo concurrentemente desde múltiples goroutines,
// agregamos un `Mutex` para sincronizar el acceso.
// Tenga en cuenta que los mutexes no deben ser copiados,
// por lo que si esta `struct` se pasa por ahí, debe hacerse
// por puntero.
type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
// Bloque el mutex antes de acceder a `counters`; desbloque
// it al final de la función utilizando una declaración [defer](defer).
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func main() {
c := Container{
// Tenga en cuenta que el valor cero de un mutex es usable tal cual,
// por lo que no es necesario inicializarlo aquí.
counters: map[string]int{"a": 0, "b": 0},
}
var wg sync.WaitGroup
// Esta función incrementa un contador nombrado
// en un bucle.
doIncrement := func(name string, n int) {
for i := 0; i < n; i++ {
c.inc(name)
}
wg.Done()
}
// Ejecute varias goroutines concurrentemente; tenga en cuenta
// que todas acceden al mismo `Container`,
// y dos de ellas acceden al mismo contador.
wg.Add(3)
go doIncrement("a", 10000)
go doIncrement("a", 10000)
go doIncrement("b", 10000)
// Espere a que las goroutines terminen
wg.Wait()
fmt.Println(c.counters)
}
Resumen
En este laboratorio, aprendimos cómo utilizar mutexes para acceder de manera segura a datos a través de múltiples goroutines. Creamos una estructura Container para almacenar un mapa de contadores y utilizamos un Mutex para sincronizar el acceso al mapa counters. También implementamos un método inc para incrementar el contador nombrado y utilizamos la estructura sync.WaitGroup para esperar a que las goroutines terminen. Finalmente, imprimimos el mapa counters utilizando la función fmt.Println.