Menu
Coddy logo textTech

sync.Mutex y sync.RWMutex

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 63 de 107.

Aunque los canales son la forma preferida de Go para coordinar goroutines, a veces necesitas proteger directamente los datos compartidos. El paquete sync proporciona mutexes: bloqueos que garantizan que solo una goroutine acceda a un recurso a la vez.

Un sync.Mutex tiene dos métodos: Lock() y Unlock(). Cuando una goroutine llama a Lock(), obtiene acceso exclusivo. Otras goroutines que llamen a Lock() se bloquearán hasta que se llame a Unlock():

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.value++
    c.mu.Unlock()
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

Usar defer c.mu.Unlock() garantiza que el bloqueo se libere incluso si la función devuelve el control antes de tiempo o entra en pánico: un patrón común y recomendado.

Cuando las lecturas son frecuentes pero las escrituras son poco frecuentes, sync.RWMutex ofrece un mejor rendimiento. Permite varios lectores simultáneamente, pero los escritores obtienen acceso exclusivo:

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()         // se permiten múltiples lectores
    defer c.mu.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()          // acceso exclusivo para escritura
    defer c.mu.Unlock()
    c.data[key] = value
}

Usa RLock()/RUnlock() para las operaciones de lectura y Lock()/Unlock() para las escrituras. Esto permite lecturas simultáneas mientras garantiza que las escrituras sean seguras.

challenge icon

Desafío

Fácil

Construyamos un sistema de inventario seguro para subprocesos que realice un seguimiento de los niveles de existencias de los productos. Tu sistema gestionará de forma segura las lecturas y escrituras simultáneas mediante mutexes, garantizando la integridad de los datos cuando varias operaciones ocurran al mismo tiempo.

Organizarás tu código en dos archivos:

  • inventory.go: Define tu sistema de gestión de inventario seguro para subprocesos.

    Crea una estructura Inventory que almacene las cantidades de los productos en un mapa y use un sync.RWMutex para proteger el acceso. Tu inventario debe admitir estas operaciones:

    • NewInventory() *Inventory - Crea un inventario nuevo con un mapa inicializado
    • AddStock(product string, quantity int) - Añade una cantidad a las existencias de un producto (usa un bloqueo exclusivo, ya que esto modifica los datos)
    • GetStock(product string) int - Devuelve las existencias actuales de un producto, o 0 si no se encuentra (usa un bloqueo de lectura, ya que esto solo lee datos)
    • RemoveStock(product string, quantity int) bool - Retira una cantidad de las existencias si hay existencias suficientes. Devuelve true si tiene éxito, false si no hay existencias suficientes (usa un bloqueo exclusivo)

    Recuerda usar defer para desbloquear y garantizar que los bloqueos siempre se liberen correctamente.

  • main.go: Lee las operaciones y muestra el funcionamiento de tu inventario seguro para subprocesos.

    Lee el número de operaciones y, después, procesa cada operación. Cada operación tiene un tipo (add, get o remove), un nombre de producto y, para las operaciones add/remove, una cantidad.

    Para cada operación, muestra el resultado:

    • add: Muestra Added [quantity] [product]
    • get: Muestra [product]: [stock] in stock
    • remove: Muestra Removed [quantity] [product] si tiene éxito, o Insufficient stock for [product] si no

Se proporcionarán las siguientes entradas:

  • Línea 1: Número de operaciones (entero)
  • Líneas siguientes: Para cada operación:
    • Tipo de operación (add, get o remove)
    • Nombre del producto
    • Cantidad (solo para las operaciones add y remove)

Por ejemplo, dada la siguiente entrada:

5
add
apples
50
get
apples
remove
apples
30
remove
apples
25
get
apples

Tu salida debería ser:

Added 50 apples
apples: 50 in stock
Removed 30 apples
Insufficient stock for apples
apples: 20 in stock

El RWMutex permite que varias llamadas a GetStock lean simultáneamente, mientras que AddStock y RemoveStock obtienen acceso exclusivo al modificar el inventario.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leer el número de operaciones
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Crear un nuevo inventario
	inventory := NewInventory()
	
	// Procesar cada operación
	for i := 0; i < numOps; i++ {
		// Leer el tipo de operación
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		// Leer el nombre del producto
		productLine, _ := reader.ReadString('\n')
		product := strings.TrimSpace(productLine)
		
		// TODO: Manejar cada tipo de operación (add, get, remove)
		// Para "add" y "remove", leer la cantidad de la entrada
		// Llamar al método de inventario apropiado
		// Imprimir el resultado según la descripción del desafío
		
		switch opType {
		case "add":
			// TODO: Leer la cantidad, añadir stock, imprimir el resultado
			
		case "get":
			// TODO: Obtener el stock, imprimir el resultado
			
		case "remove":
			// TODO: Leer la cantidad, eliminar stock, imprimir el resultado apropiado
			
		}
	}
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Go online