SPIEGAZIONE DETTAGLIATA DEL MUTEX ================================= Un MUTEX è un "lucchetto" che protegge le risorse condivise da accessi simultanei. MUTEX = "Mutual Exclusion" (Esclusione Mutua) Idea semplice: - Solo UN thread per volta può accedere a una risorsa protetta - Gli altri aspettano fuori OPERAZIONI BASE: ================ 1. pthread_mutex_lock(&mutex) → BLOCCA il lucchetto - Se il mutex è LIBERO → lo prendi e continui - Se il mutex è OCCUPATO → ti blocchi e aspetti 2. pthread_mutex_unlock(&mutex) → SBLOCCA il lucchetto - Liberi il lock - Se qualcuno era in attesa, uno di loro lo prende DIFFERENZA TRA SEMAFORO E MUTEX: ================================ SEMAFORO: - È un CONTATORE (0, 1, 2, 3, ...) - Può essere decrementato/incrementato da chiunque - Usato per SINCRONIZZAZIONE (farsi attendere) MUTEX: - È un LUCCHETTO (OCCUPATO o LIBERO) - Massimo 1 thread per volta - Usato per PROTEGGERE DATI CONDIVISI ESEMPIO PRATICO - SENZA MUTEX (CAOS!): ====================================== int contatore = 0; Thread 1: Thread 2: temp1 = contatore (0) temp2 = contatore (0) ← Legge lo stesso valore! temp1 = temp1 + 1 (1) temp2 = temp2 + 1 (1) contatore = temp1 (1) contatore = temp2 (1) ❌ Risultato: contatore = 1 (dovrebbe essere 2!) Perché? Entrambi hanno letto 0 contemporaneamente ESEMPIO PRATICO - CON MUTEX: ============================= int contatore = 0; pthread_mutex_t mutex; Thread 1: pthread_mutex_lock(&mutex) ← Prende il lucchetto temp1 = contatore (0) temp1 = temp1 + 1 (1) contatore = temp1 (1) pthread_mutex_unlock(&mutex) ← Rilascia il lucchetto ↓ Thread 2 finalmente può entrare Thread 2: pthread_mutex_lock(&mutex) ← Prende il lucchetto temp2 = contatore (1) ← Legge il valore corretto! temp2 = temp2 + 1 (2) contatore = temp2 (2) pthread_mutex_unlock(&mutex) ← Rilascia il lucchetto ✓ Risultato: contatore = 2 (CORRETTO!) SEZIONE CRITICA: ================ Una SEZIONE CRITICA è una parte di codice dove si accede a dati condivisi. SENZA PROTEZIONE: while(1) { x = buffer[i]; ← SEZIONE CRITICA ⚠️ ... } Problema: Due thread potrebbe leggere lo stesso indice contemporaneamente! CON MUTEX: while(1) { pthread_mutex_lock(&mutex); x = buffer[i]; ← SEZIONE CRITICA PROTETTA ✓ ... pthread_mutex_unlock(&mutex); } Soluzione: Solo uno per volta entra nella sezione critica VISUALIZZAZIONE TEMPORALE: =========================== SENZA MUTEX (RACE CONDITION): TEMPO │ Thread A │ Thread B │ Variabile condivisa ─────┼────────────────────┼────────────────────┼────────────────── t0 │ leggi dati = 5 │ (in attesa) │ dati = 5 t1 │ (in attesa) │ leggi dati = 5 │ dati = 5 t2 │ calcola = 5 + 1 │ calcola = 5 + 1 │ t3 │ scrivi dati = 6 │ (in attesa) │ dati = 6 t4 │ │ scrivi dati = 6 │ dati = 6 │ │ │ ❌ PROBLEMA: Entrambi hanno letto 5, e hanno scritto 6 (invece di 7!) CON MUTEX (SEZIONE CRITICA): TEMPO │ Thread A │ Thread B │ Variabile ─────┼────────────────────────────┼────────────────────────────┼────────── t0 │ pthread_mutex_lock() │ (in attesa del lock!) │ dati = 5 │ ← Ha il lucchetto │ │ t1 │ leggi dati = 5 │ (in attesa del lock) │ dati = 5 t2 │ calcola = 5 + 1 │ (in attesa del lock) │ t3 │ scrivi dati = 6 │ (in attesa del lock) │ dati = 6 t4 │ pthread_mutex_unlock() │ (in attesa del lock) │ │ ← Ha rilasciato il lock │ │ │ │ pthread_mutex_lock() │ │ │ ← Ha il lucchetto ora │ t5 │ (continuano altro) │ leggi dati = 6 │ dati = 6 t6 │ │ calcola = 6 + 1 │ t7 │ │ scrivi dati = 7 │ dati = 7 t8 │ │ pthread_mutex_unlock() │ ✓ CORRETTO: Entrambi hanno lavorato in sequenza, risultato = 7 NEL TUO CODICE (prod/cons): =========================== int buffer[20]; int posW = 0; int posR = 0; pthread_mutex_t mutex; void *prod(void * arg) { while(1){ sem_wait(&SP); x = rand() % 90; pthread_mutex_lock(&mutex); ← Prende il lucchetto buffer[posW] = x; ← SEZIONE CRITICA posW = (posW+1) % 20; ← SEZIONE CRITICA pthread_mutex_unlock(&mutex); ← Rilascia il lucchetto printf("PRODOTTO: %d\n", x); sem_post(&SC); Sleep(100); } } void *cons(void * arg) { while(1){ sem_wait(&SC); pthread_mutex_lock(&mutex); ← Prende il lucchetto x = buffer[posR]; ← SEZIONE CRITICA posR = (posR+1) % 20; ← SEZIONE CRITICA pthread_mutex_unlock(&mutex); ← Rilascia il lucchetto printf("CONSUMATO: %d\n", x); sem_post(&SP); Sleep(rand() % 1000 + 500); } } ❓ Cosa protegge il mutex? ✓ Protegge buffer[], posW, posR da accessi simultanei ✓ Garantisce che prod e cons non leggano/scrivono contemporaneamente INIZIALIZZAZIONE: ================= pthread_mutex_init(&mutex, NULL); - Primo parametro: il mutex - Secondo parametro: attributi (NULL = attributi di default) DISTRUZIONE: ============ pthread_mutex_destroy(&mutex); Fatto quando non serve più (raggiunta la fine del main) REGOLA D'ORO: ============= Ogni volta che più thread accedono a una STESSA VARIABILE: ✓ Usa un MUTEX per proteggerla ✓ Lock prima di accedere ✓ Unlock dopo aver finito CODICE SBAGLIATO: pthread_mutex_unlock(&mutex); ← Rilascio prima di finire! x = buffer[i]; ← Thread B potrebbe modificare buffer! CODICE CORRETTO: x = buffer[i]; pthread_mutex_unlock(&mutex); ← Rilascio DOPO DEADLOCK (ATTENZIONE!): ======================= Se fai due lock senza unlock, il programma si blocca FOREVER! ❌ SBAGLIATO: pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); ← Lo stesso thread aspetta se stesso! DEADLOCK! CONCLUSIONE: ============ SEMAFORI → Sincronizzazione ("aspetta che finisca") MUTEX → Protezione dei dati (esclusione mutua) Nel tuo gen.c: - Usi SEMAFORI per sincronizzare (il generatore aspetta cubo e quadrato) Nel tuo prod/cons: - Usi SEMAFORI per sincronizzare (prod aspetta spazio, cons aspetta dati) - Usi MUTEX per proteggere il buffer da accessi simultanei Semplice? È come un cesso pubblico con una sola porta: - Tutti aspettano in coda (SEMAFORO) - Solo uno può entrare per volta (MUTEX)