Informatica 3 Informatica 3 LEZIONE 6: Il controllo dell esecuzione Modulo 1: La gestione delle eccezioni Modulo 2: Programmazione concorrente Lezione 6 - Modulo 1 La gestione delle eccezioni Politecnico di Milano - Prof. Sara Comai 1 Politecnico di Milano - Prof. Sara Comai 2 Il controllo dell esecuzione Il controllo dell esecuzione può avvenire tramite: Strutture di controllo a livello di istruzioni Esecuzione condizionale: If-then-else, Case Iterazione: While, For, Repeat Strutture di controllo a livello di unità di esecuzione (procedura, modulo) Chiamata a funzione e ritorno Gestione delle eccezioni Altre strutture di controllo utilizzate nella programmazione concorrente Errori durante l esecuzione Durante l esecuzione di un programma si possono verificare delle condizioni per cui il programma è errato Indice dell array che eccede i limiti Divisione per zero Radice quadrata di un numero negativo Quantità di memoria da allocare per il programma non disponibile Utilizzo di strutture di controllo convenzionali: Ogni volta che si accede all array occorre testare il valore dell indice Ogni volta che si esegue una divisione occorre verificare se il divisore è 0 Politecnico di Milano - Prof. Sara Comai 3 Politecnico di Milano - Prof. Sara Comai 4
Eccezioni Gestione delle eccezioni: Eccezione: comportamento anomalo non desiderato che accade raramente I linguaggi di programmazione moderni permettono di gestire le eccezioni Definizione di un eccezione Definizione dell azione da intraprendere (exception handler) La gestione delle anomalie può essere spostata al di fuori del flusso principale del programma Definizione delle eccezioni Trattamento delle eccezioni nei linguaggi di programmazione: Quali sono le eccezioni da trattare? Quali unità del programma possono generare un eccezione e come? Come e dove definire un exception handler? Come si passa il controllo dall eccezione al suo exception handler? Come prosegue l esecuzione una volta che l eccezione è stata gestita? Politecnico di Milano - Prof. Sara Comai 5 Politecnico di Milano - Prof. Sara Comai 6 Gestione delle eccezioni in C++ Le eccezioni possono essere generate Dall ambiente di esecuzione (es. divisione per zero) Esplicitamente dal programma: try, catch e throw Il codice da controllare deve essere contenuto in un blocco try Se all interno di un blocco try si verifica un eccezione, questa viene lanciata tramite l istruzione throw: trasferisce un oggetto al gestore dell eccezion L eccezione viene raccolta da una catch ed elaborata Divisione tra due interi: Esempio void dividi (int a, int b){ //blocco try if (!b) throw b; //eccezione cout << Risultato della divisione: << a/b << endl; catch (int i) { //azione da intraprendere cout << è stata effettuata una divisione per zero << endl; Politecnico di Milano - Prof. Sara Comai 7 Politecnico di Milano - Prof. Sara Comai 8
Il controllo del programma Try e catch Una volta eseguita la catch il controllo del programma passa all istruzione successiva alla try Non funzione come una chiamata di funzione! void dividi (int a, int b){ //blocco try if (!b) throw b; //eccezione cout << Risultato della divisione: << a/b << endl; catch (int i) { //azione da intraprendere cout << è stata effettuata una divisione per zero << endl; Se l errore non può essere corretto all interno della catch si fa terminare il programma (exit(), abort()) Politecnico di Milano - Prof. Sara Comai 9 Ad un blocco try possono essere associate più catch, ognuna per un tipo diverso Viene eseguita la catch del tipo specificato // blocco try... throw 1;... throw c ; catch (int i) {... // catch per tipo intero catch (char c) {... // catch per tipo carattere Se si lancia un eccezione per la quale non esistono catch il programma termina in modo anormale Politecnico di Milano - Prof. Sara Comai 10 Raccolta di tutte le eccezioni Restrizione del tipo di eccezioni Per raccogliere tutte le eccezioni e non solo quelle di un determinato tipo: catch( ) { //lista di argomenti di lungh. variabile //elaborazione delle eccezioni Esempio: try{ if (!b) throw b; //lancia un intero if (b==1) throw a ; //lancia un carattere; if (b==2) throw 12.12; //lancia un double; catch(int i) { cout << Eccezione intera << endl; catch( ) { cout << Eccezione non intera << endl; Per le funzioni richiamate all interno di un blocco try è possibile restringere il tipo di eccezioni che la funzione può lanciare Se si cerca di sollevare un eccezione non presente nell interfaccia viene richiamata automaticamente la funzione unexpected(): il suo comportamento di default (ridefinibile) chiama abort(); funzione(); catch{ //blocco catch void funzione (void) throw (int, char) { //corpo funzione throw () se si desidera che la funzione non lanci eccezioni Se non c è nulla può lanciare qualsiasi eccezione Politecnico di Milano - Prof. Sara Comai 11 Politecnico di Milano - Prof. Sara Comai 12
Rilancio di un eccezione Conclusioni All interno del gestore dell eccezione (catch) si possono rilanciare eccezioni, richiamando throw senza specificare nè l errore nè il tipo L eccezione corrente viene passata a una sequenza trycatch più esterna void main (){ int a=0; funzione(a); catch (int i) { void funzione(int b){ if (!b) throw b; catch (int i) { throw; //rilancia l eccezione passando l intero Politecnico di Milano - Prof. Sara Comai 13 Quali sono le eccezioni da trattare? In quasi tutti i linguaggi: eccezioni built-in ed eccezioni definite dal programmatore Quali unità del programma possono generare un eccezione e come? In C++ (e Ada) le eccezioni possono essere associate a qualsiasi blocco. In altri linguaggi solamente a routine (es. Eiffel). In C++ l eccezione viene lanciata con l istruzione throw Come e dove definire un exception handler? In C++ viene definito tramite l istruzione catch, dopo il blocco try Come si passa il controllo dall eccezione al suo exception handler? In C++ (e Ada) viene passato all handler appropriato tramite propoagazione. Come prosegue l esecuzione una volta che l eccezione è stata gestita? In C++ (e Ada) ripresa dall istruzione successiva al blocco interrotto Ripresa dell esecuzione (PL/1): da dove il programma è stato interrotto Politecnico di Milano - Prof. Sara Comai 14 Informatica 3 Lezione 6 - Modulo 2 Programmazione concorrente Programmazione concorrente I linguaggi per la programmazione concorrente permettono di strutturare il software in unità concorrenti da eseguire in parallelo Sistemi multiprocessori Sistemi distribuiti con computer che cooperano Sistemi monoprocessori Parallelismo fisico Parallelismo logico Le unità concorrenti prendono il nome di processi Thread: processi che condividono un singolo spazio di indirizzamento (accesso allo stesso ambiente globale) Politecnico di Milano - Prof. Sara Comai 15 Politecnico di Milano - Prof. Sara Comai 16
Problema dei 5 filosofi Problema dei 5 filosofi (2) Problema posto da Edsger Dijkstra nel 1971 Illustra gli aspetti di base del multithreading Problema: 5 filosofi seduti intorno ad un tavolo ognuno ha di fronte un piatto di spaghetti e tra un piatto e l altro si trova una forchetta nella vita i filosofi alternano periodi in cui pensano a periodi in cui mangiano per mangiare gli spaghetti hanno bisogno di 2 forchette Politecnico di Milano - Prof. Sara Comai 17 Quando il filosofo vuole mangiare deve acquisire 2 forchette, una alla volta se riesce ad acquisirle mangia per un po e poi le rimette al loro posto e ricomincia a pensare Si tratta di un problema di allocazione delle risorse: dobbiamo assegnare risorse ai processi (forchette ai filosofi) necessarie per compiere il loro lavoro Problema generalizzato: un insieme finito di thread condivide un insieme finito di risorse e ogni risorsa può essere usata solamente da un thread alla volta Politecnico di Milano - Prof. Sara Comai 18 Sincronizzazione Mutua esclusione Il problema fondamentale è sincronizzare l accesso dei filosofi alle forchette Soluzione più semplice: i filosofi (thread) sincronizzano l accesso alle forchette (risorse) usando memoria condivisa per acquisire una forchetta un filosofo deve testare un flag condiviso che dice se la forchetta è già in uso o meno il filosofo procede se il flag indica che la forchetta non è in uso dopo aver acquisito la forchetta il filosofo modifica il valore del flag Problema di questa soluzione: tra l istante in cui un filosofo testa il valore del flag e l istante in cui ne modifica il valore per indicare che la forchetta non è disponibile altri filosofi potrebbero procedere ad acquisire la forchetta --> problema di race condition (corsa critica) occorre un metodo per proteggere i flag condivisi, in modo tale che gli accessi al flag siano mutuamente esclusivi Flag mutuamente esclusivo: può essere acceduto in un determinato momento solamente da un thread, che è l unico che può modificarne il valore Uno dei metodi per ottenere mutua esclusione: controllare l accesso alle risorse condivise tramite semafori La soluzione tramite semafori risolve il problema della mutua esclusione e anche dell attesa dei filosofi quando le forchette non sono disponibili Politecnico di Milano - Prof. Sara Comai 19 Politecnico di Milano - Prof. Sara Comai 20
Semafori Implementazione Dijkstra, 1965: semaforo come meccanismo per sincronizzare thread concorrenti Due operazioni primitive: P (dall olandese proberen = testare): testa lo stato del semaforo e restituisce il controllo al thread se la risorsa è disponibile, altrimenti mantiene il thread in attesa V (dall olandese verhogen = incrementare): rilascia la risorsa controllata dal semaforo; la risorsa diventa disponibile ad altri thread Esistono diversi tipi di semafori: Semafori basati su conteggio: esiste un pool di risorse ed il semaforo tiene il conto di quante risorse sono disponibili Semafori binari: vi può accedere un thread alla volta Semafori mutex: semafori binari che permettono di gestire problemi di mutua esclusione Politecnico di Milano - Prof. Sara Comai 21 long WINAPI philosopher(long n) { /* thread che rappresenta un filosofo */ for (;;) { think(n); WaitForSingleObject(fork[(n + 1) % N], INFINITE); Sleep(0); WaitForSingleObject(fork[n], INFINITE); eat(n); ReleaseMutex(fork[(n + 1) % N]); ReleaseMutex(fork[n]); P sulla forchetta di destra P sulla forchetta di sinistra V sulle due forchette Politecnico di Milano - Prof. Sara Comai 22 Blocco critico Blocco individuale Problema della soluzione basata su semafori: può accadere che tutti i filosofi abbiano una sola forchetta - rimangono tutti in attesa della seconda forchetta --> problema del blocco critico (deadlock) Possibile soluzione: il filosofo prende la forchetta di sinistra, testa se la forchetta di destra è disponibile o meno se non è disponibile entro un certo periodo di tempo rilascia la forchetta di sinistra attende per un certo periodo di tempo (casuale) e poi riprova Politecnico di Milano - Prof. Sara Comai 23 Altro problema: blocco individuale (starvation) può accadere che un filosofo non riesca mai ad accedere alle forchette il tempo di attesa è non-deterministico: non si può sapere in anticipo quant è il tempo massimo di attesa per una risorsa Soluzione: adottare politiche di allocazione globali ad es. politiche di priorità che dipendono dal tempo di attesa Politecnico di Milano - Prof. Sara Comai 24
Conclusioni La programmazione multi-thread è molto più difficile della programmazione sequenziale Richiede di conoscere i meccanismi di sincronizzazione e comunicazione tra thread, come i diversi tipi di semafori, monitor, sezioni critiche, segnali, messaggi, code, mailbox, ecc. I linguaggi di programmazione che supportano in modo nativo la programmazione concorrente forniscono le primitive per gestire la creazione di processi e la loro sincronizzazione In altri linguaggi è possibile simularla Politecnico di Milano - Prof. Sara Comai 25