Politecnico di Milano - Prof. Sara Comai 1
LEZIONE 6: Il controllo dell’esecuzione
• Modulo 1: La gestione delle eccezioni
• Modulo 2: Programmazione concorrente
Informatica 3
Politecnico di Milano - Prof. Sara Comai 2
Lezione 6 - Modulo 1
La gestione delle eccezioni
Informatica 3
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 5
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
Politecnico di Milano - Prof. Sara Comai 6
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?
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
Esempio
Divisione tra due interi:
void dividi (int a, int b){
try { //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 9
Il controllo del programma
• 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){
try { //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 10
Try e catch
• Ad un blocco try possono essere associate più catch, ognuna per un tipo diverso
– Viene eseguita la catch del tipo specificato
try { // 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
Raccolta di tutte le 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; }
Restrizione del tipo di eccezioni
• 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();
try { 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 13
Rilancio di un’eccezione
• 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 try- catch più esterna
void funzione(int b){
try { if (!b) throw b;
//…
}
catch (int i) { //…
throw; //rilancia l’eccezione passando l’intero }
} void main (){
int a=0;
//…
try { funzione(a);
//…
}
catch (int i) { //…
} }
Politecnico di Milano - Prof. Sara Comai 14
Conclusioni
• 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
Lezione 6 - Modulo 2
Programmazione concorrente
Informatica 3 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
• Le unità concorrenti prendono il nome di processi
• Thread: processi che condividono un singolo spazio
di indirizzamento (accesso allo stesso ambiente globale)
Parallelismo fisico Parallelismo logico
Politecnico di Milano - Prof. Sara Comai 17
Problema dei 5 filosofi
• Problema posto da Edsger Dijkstra nel 1971
• Illustra gli aspetti di base del multi- threading
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 18
Problema dei 5 filosofi (2)
– 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
Sincronizzazione
– 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
Mutua esclusione
– 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 21
Semafori
• 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 22
Implementazione
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
Blocco critico
– 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
Blocco individuale
– 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 25