Lección 15 · 15 de 20
Mutex y estado compartido · Go
Proteja datos compartidos por varias goroutines en Go con sync.Mutex: 55 compradores, 40 entradas y ninguna vendida dos veces.

Cuando varias goroutines leen y escriben la misma variable a la vez hay una condición de carrera. En Buy, comprobar si quedan entradas y descontar una son dos pasos: si dos compradores comprueban a la vez y ven 1, los dos la compran y se vende una entrada que no existe.
`sync.Mutex` (de *mutual exclusion*) lo impide. b.mu.Lock() deja pasar a una sola goroutine; las demás esperan en esa línea hasta que la primera llama a Unlock. Lo que queda entre las dos llamadas, la sección crítica, se ejecuta de una en una, así que comprobar y descontar ocurren juntos.
defer b.mu.Unlock() justo después de Lock es la forma habitual: el cerrojo se suelta al salir de la función, salga como salga. El mutex vive dentro del struct, junto a los datos que protege, y el comentario lo dice. Los métodos usan un receptor puntero: copiar un Mutex lo estropea (y go vet lo avisa).
En main, 55 goroutines compran a la vez y wg.Wait() espera a todas. Leer office.sold después sin el cerrojo es correcto, porque Wait garantiza que todas han terminado. El resultado no depende del orden: siempre se venden 40, se rechazan 15 y no queda ninguna.
Para detectar carreras, ejecute go run -race main.go: avisa si dos goroutines tocan lo mismo sin protección. Si casi todo son lecturas, existe sync.RWMutex; para un simple contador, sync/atomic.
Código · main.go
package mainimport ( "fmt" "sync")type BoxOffice struct { // shared by many goroutines: mu protects the fields below mu sync.Mutex tickets map[string]int sold int}func (b *BoxOffice) Buy(show string) { b.mu.Lock() // only one goroutine at a time gets past Lock defer b.mu.Unlock() // released when Buy returns if b.tickets[show] > 0 { // check and update together: no ticket is sold twice b.tickets[show]-- b.sold++ }}func main() { office := &BoxOffice{tickets: map[string]int{"jazz night": 40}} var wg sync.WaitGroup for i := 0; i < 55; i++ { // 55 buyers arrive at the same time wg.Add(1) go func() { defer wg.Done(); office.Buy("jazz night") }() } wg.Wait() fmt.Println("buyers: 55 | sold:", office.sold, "| turned away:", 55-office.sold) fmt.Println("tickets left:", office.tickets["jazz night"])}Salida · go run main.go
buyers: 55 | sold: 40 | turned away: 15 tickets left: 0
Para recordar
- Dos goroutines que escriben lo mismo sin protección son una carrera.
Lockdeja pasar a una sola goroutine hasta elUnlock.- Escriba
defer mu.Unlock()justo después deLock(). - Ponga el mutex junto a los datos que protege y use receptor puntero.
go run -racedetecta condiciones de carrera.
Ejercicio
Añada a la taquilla un segundo concierto, "rock night", con 10 entradas, y haga que los compradores pares pidan uno y los impares el otro. Escriba al final lo que queda de cada uno.