Menu
Coddy logo textTech

sync.Mutex e sync.RWMutex

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 63 di 107.

Sebbene i canali siano il metodo preferito in Go per coordinare le goroutine, a volte è necessario proteggere direttamente i dati condivisi. Il pacchetto sync fornisce i mutex: blocchi che garantiscono che solo una goroutine alla volta acceda a una risorsa.

Un sync.Mutex ha due metodi: Lock() e Unlock(). Quando una goroutine chiama Lock(), ottiene l'accesso esclusivo. Le altre goroutine che chiamano Lock() rimarranno bloccate finché non viene chiamato 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
}

Usare defer c.mu.Unlock() garantisce che il lock venga rilasciato anche se la funzione termina in anticipo o va in panico: un modello comune e consigliato.

Quando le letture sono frequenti ma le scritture rare, sync.RWMutex offre prestazioni migliori. Consente a più lettori di accedere contemporaneamente, ma agli scrittori concede l’accesso esclusivo:

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}
func (c *Cache) Get(key string) string {
    c.mu.RLock()         // sono consentiti più lettori contemporaneamente
    defer c.mu.RUnlock()
    return c.data[key]
}
func (c *Cache) Set(key, value string) {
    c.mu.Lock()          // accesso esclusivo per la scrittura
    defer c.mu.Unlock()
    c.data[key] = value
}

Usa RLock()/RUnlock() per le operazioni di lettura e Lock()/Unlock() per le scritture. Questo consente letture concorrenti garantendo al contempo la sicurezza delle scritture.

challenge icon

Sfida

Facile

Costruiamo un sistema di inventario sicuro per l’uso concorrente che tiene traccia dei livelli di scorte dei prodotti. Il sistema gestirà in sicurezza letture e scritture concorrenti usando mutex, garantendo l’integrità dei dati quando più operazioni avvengono simultaneamente.

Organizzerai il codice in due file:

  • inventory.go: definisci il sistema di gestione dell’inventario sicuro per l’uso concorrente.

    Crea una struct Inventory che memorizza le quantità dei prodotti in una mappa e usa un sync.RWMutex per proteggere l’accesso. L’inventario deve supportare queste operazioni:

    • NewInventory() *Inventory - Crea un nuovo inventario con una mappa inizializzata
    • AddStock(product string, quantity int) - Aggiunge la quantità alle scorte di un prodotto (usa un blocco esclusivo perché modifica i dati)
    • GetStock(product string) int - Restituisce le scorte attuali di un prodotto, o 0 se non viene trovato (usa un blocco di lettura perché legge soltanto i dati)
    • RemoveStock(product string, quantity int) bool - Rimuove la quantità dalle scorte se sono sufficienti. Restituisce true se l’operazione riesce, false se le scorte sono insufficienti (usa un blocco esclusivo)

    Ricorda di usare defer per sbloccare, così da assicurarti che i blocchi vengano sempre rilasciati correttamente.

  • main.go: leggi le operazioni e mostra il funzionamento del tuo inventario sicuro per l’uso concorrente.

    Leggi il numero di operazioni, poi elaborale una alla volta. Ogni operazione ha un tipo (add, get o remove), il nome di un prodotto e, per le operazioni add/remove, una quantità.

    Per ogni operazione, stampa il risultato:

    • add: stampa Added [quantity] [product]
    • get: stampa [product]: [stock] in stock
    • remove: stampa Removed [quantity] [product] se l’operazione riesce, oppure Insufficient stock for [product] in caso contrario

Verranno forniti i seguenti input:

  • Riga 1: numero di operazioni (intero)
  • Righe successive: per ogni operazione:
    • Tipo di operazione (add, get o remove)
    • Nome del prodotto
    • Quantità (solo per le operazioni add e remove)

Ad esempio, dato:

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

L’output dovrebbe essere:

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

RWMutex consente a più chiamate a GetStock di leggere contemporaneamente, mentre AddStock e RemoveStock ottengono l’accesso esclusivo quando modificano l’inventario.

Provalo tu

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leggi il numero di operazioni
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Crea un nuovo inventario
	inventory := NewInventory()
	
	// Elabora ogni operazione
	for i := 0; i < numOps; i++ {
		// Leggi il tipo di operazione
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		// Leggi il nome del prodotto
		productLine, _ := reader.ReadString('\n')
		product := strings.TrimSpace(productLine)
		
		// TODO: Gestisci ogni tipo di operazione (add, get, remove)
		// Per "add" e "remove", leggi la quantità dall'input
		// Chiama il metodo appropriato dell'inventario
		// Stampa il risultato secondo la descrizione dell'esercizio
		
		switch opType {
		case "add":
			// TODO: Leggi la quantità, aggiungi le scorte, stampa il risultato
			
		case "get":
			// TODO: Ottieni le scorte, stampa il risultato
			
		case "remove":
			// TODO: Leggi la quantità, rimuovi le scorte, stampa il risultato appropriato
			
		}
	}
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Go online