capitolo 23 – problema del barbiere
Transcript
capitolo 23 – problema del barbiere
CAPITOLO 23 – PROBLEMA DEL BARBIERE NEGOZIO DEL BARBIERE (20 norma 50 clienti) La figura riporta una pianta della sala del barbiere e come si vede ipotizza che ci siano presenti tre sedie per il taglio e che tre barbieri operano nella sala, nella sala c’è un divano di capienza di massimo quattro persone sedute e uno spazio per altre persone in piedi. Inoltre la sala è dotata di una cassa per effettuare i pagamenti. Il numero massimo di persone presenti nel negozio non può essere più di venti per via delle limitazioni di legge, ma si suppone che ci sia la possibilità di soddisfare fino a 50 clienti. Quando il negozio contiene il numero massimo di clienti, nessun nuovo cliente può entrare, l’eccedenza è evidente che rimane in attesa fuori.. Quando entra un cliente se c’è posto si siede sul divano, altrimenti rimane in piedi; quando un barbiere è libero, il primo ad essere servito è il cliente che ha atteso più a lungo sul divano e che quindi si porta ad una sedia barbiere libera. Ovviamente ogni volta che un cliente transita per passare dal divano ad una sedia barbiere, uno in piedi occupa il posto sul divano. Quando il barbiere termina informa di aver finito il cliente il quale essendo stato servito, va alla cassa per il pagamento. Poiché la cassa è unica, un solo cliente per volta può pagare. SIGNIFICATO DEI SEMAFORI La tabella riporta i semafori utilizzati per la gestione del problema del barbiere e il significato delle primitive wait e signal che agiscono sul semaforo stesso. Il semaforo capacita_max gestisce la capacità del negozio in termini di numero di clienti presenti. La primitiva wait(capacita_max) consente al cliente che aspetta che ci sia posto nel negozio di poter entrare, signal(capacita_max) consente invece ad un cliente che esce dal negozio di informare che ora c’è un nuovo posto e quindi un nuovo cliente può entrare. 1 Il semaforo divano coordina il sedersi e l’alzarsi dei clienti dal divano. wait(divano) consente ad un cliente in attesa di sedersi, signal(divano) è la primitiva con la quale chi si alza dal divano consente ad un cliente in piedi di accomodarsi. Il semaforo sedia_barbiere è utilizzato per l’uso corretto delle tre sedie dei barbieri assicurando che non più di tre clienti alla volta siano serviti contemporaneamente. La primitiva wait(sedia_barbiere) fa si che un cliente che aspetta che si liberi una sedia del barbiere possa accomodarsi, invece signal(sedia_barbiere) fa si che un nuovo cliente si porti dal divano alla sedia per essere servito. Il semaforo cliente_pronto è necessario per assicurarsi che ci siano clienti seduti sulle sedie dei barbieri pronti per il taglio. Con la primitiva wait(cliente_pronto) il barbiere aspetta che ci sia un cliente seduto, con signal(cliente_pronto) il cliente avvisa il barbiere di essersi seduto. Il semaforo finito gestisce il tempo in cui il cliente rimane seduto per il taglio, con wait(finito) il cliente aspetta che il taglio sia terminato mentre con signal(finito) il barbiere avvisa quando il taglio è terminato. Il semaforo lascia_sedia_b serve al cliente per informare di aver lasciato la sedia e di conseguenza il barbiere lancerà signal( sedia barbiere). Un nuovo cliente si potrà accomodare mentre che il barbiere si porta alla cassa. Con la primitiva wait(lascia_sedia_b) il barbiere aspetta che il cliente si alzi, invece con signal(lascia_sedia_b) il cliente avvisa il barbiere quando si alza dalla sedia. Il semaforo pagamento è necessario per il pagamento del cliente al barbiere presso l’unica cassa presente nel negozio, per cui il pagamento avviene un cliente alla volta. La primitiva wait(pagamento) permette che il cassiere aspetti che il cliente paghi invece con signal(pagamento) il cliente avvisa il cassiere che ha pagato. Il semaforo ricevuta è utilizzato al fine di rilasciare la ricevuta al cliente per il taglio effettuato. Dunque con la wait(ricevuta) il cliente aspetta una ricevuta, con signal(ricevuta) il cassiere segnala di aver ricevuto il pagamento. Infine poiché ai barbieri è affidato anche il compito di cassieri, il semaforo coord è utilizzato sia per il controllo dei barbieri che devono effettuare i tagli sia per le operazioni di cassa. Con wait(coord) si aspetta che il barbiere termini il taglio o che la cassa deve incassare il pagamento, con signal(coord) si segnala che c’è un barbiere disponibile o che la cassa abbia ricevuto il pagamento e che può rilasciare la ricevuta. ALGORITMO DEL BARBIERE INGIUSTO program barbiere1; var capacita_max: semaforo (:=20); divano: semaforo (:=4); sedia_barbiere, coord: semaforo (:=3); cliente_pronto, finito, lascia_sedia_b, pagamento, ricevuta: semaforo (:=0); procedure cliente; begin wait(capacita_max); entra nel negozio; wait(divano); siedi sul divano; wait(sedia_barbiere); alzati dal divano; signal(divano); siedi sulla sedia del barbiere; signal(cliente_pronto); wait(finito); lascia la sedia del barbiere; signal(lascia_sedia_b); paga; signal(pagamento); wait(ricevuta); esci dal negozio; signal (capacita_max); end; procedure barbiere; begin La slide presenta l’algoritmo del barbiere ingiusto. Il codice presenta la definizione e l’inizializzazione delle variabili semaforo. Il programma principale attiva 50 clienti, 3 barbieri e il processo cassiere. La procedura cliente coordinata tramite le primitive wait e signal effettua le seguenti operazioni: entra nel negozio; siedi sul divano; alzati dal divano; siedi sulla sedia del barbiere; lascia la sedia del barbiere; paga; esci dal negozio. Ognuna di queste operazioni coinvolge delle risorse che devono essere gestite garantendone la mutua esclusione per cui è necessario l’utilizzo di semafori che debbano precedere e seguire ognuna di queste operazioni. L’azione entra nel negozio è preceduta da wait(capacita_max) per cui il cliente aspetta che ci sia posto nel negozio prima di entrare, altrettanto l’operazione esci dal negozio è seguita da una signal(capacita_max) che avvisa il prossimo cliente di poter entrare. Una volta entrato nel negozio, il cliente ha necessità di sedersi sul divano per cui effettua una wait(divano) con cui il cliente aspetta che si liberi un posto sul divano una volta ottenuto si siede con siedi sul divano ed effettua una wait(sedia_barbiere) con cui aspetta che si liberi una sedia del barbiere. Ottenuta questa si alza dal 2 repeat wait(cliente_pronto); wait(coord); taglia i capelli; signal(coord); signal(finito); wait(lascia_sedia_b); signal(sedia_barbiere); forever end; procedure cassiere; begin repeat wait(pagamento); wait(coord); prendi il denaro; signal(coord); signal(ricevuta); forever end; begin (*programma principale*) parbegin cliente; …50 volte; …cliente; barbiere; barbiere; barbiere; cassiere; parend; end. divano con l’operazione alzati dal divano ed effettua una signal(divano) con cui avvisa il prossimo cliente che aspetta di sedersi che un posto si è liberato. Il cliente a questo punto effettua l’operazione siedi sulla sedia del barbiere ed esegue una signal(cliente_pronto) che avvisa il barbiere che il cliente si è seduto. Con wait(finito) il cliente aspetta che il taglio sia finito e segue l’operazione di lascia la sedia del barbiere. Con la primitiva signal(lascia_sedia_b) il cliente avvisa il barbiere che si è alzato, successivamente effettua il pagamento, con l’operazione paga, emette una signal(pagamento) con cui avvisa il cassiere che sta pagando ed esegue wait(ricevuta) con cui si pone in attesa che il cassiere segnali di aver accettato il pagamento. La procedura barbiere presenta in un ciclo infinito l’operazione fondamentale di taglia i capelli, tale operazione è preceduta e seguita da una serie di primitive che gestiscono i vari semafori. Inizialmente viene effettuata la primitiva wait (cliente_pronto) con cui il barbiere aspetta che un cliente si sieda, successivamente con wait(coord) si coordina con gli altri barbieri e si porta ala taglio dei capelli. A questo punto avviene l’operazione di taglia i capelli e successivamente vengono lanciate le primitive signal(coord) e signal(finito) con cui rispettivamente si segnala che c’è un barbiere disponibile e con cui il barbiere avvisa il cliente che il taglio è terminato. Con wait(lascia_sedia_b) il barbiere aspetta che il cliente si alzi dalla sedia ed infine con signal(sedia_barbiere) avvisa che una propria sedia è vuota. La procedura cassiere effettua in un ciclo infinito l’operazione di prendi il denaro, tale operazione è preceduta dalle primitive wait(pagamento) e wait(coord) con cui rispettivamente il cassiere aspetta che il cliente paghi e con cui si aspetta che un barbiere sia libero per fare il cassiere. L’istruzione prendi il denaro è poi seguita da signal(coord) e signal(ricevuta) con cui rispettivamente si segnala che c’è un barbiere disponibile e con cui il cassiere segnala di aver accettato il pagamento. TRATTAMENTO INGIUSTO DEI CLIENTI Supponiamo che tre clienti sono in attesa di finito, dovrebbero alzarsi nell'ordine in cui si sono seduti ma se uno dei tre finisce prima il suo signal finito, costringe l'altro che si è seduto prima ad uscire! L’algoritmo presentato provoca un trattamento ingiusto dei clienti a causa di un problema di temporizzazione. Supponiamo che ci siano tre clienti seduti su tre sedie per il taglio, secondo la politica First-In-First-Out con cui sono gestite le code di attesa attraverso la primitiva wait(finito) Questo si è generato per mancanza di corrispondenza tra il primo cliente che ha occupato la sedia del barbiere deve cliente e processocliente e di conseguenza tra barbiere e essere il primo a lasciarla. Questo provoca il fatto che se cliente. un cliente ha terminato prima perché ha pochi capelli o perché il barbiere ha lavorato più velocemente non può alzarsi se il cliente che si è seduto prima di lui non ha terminato o nel caso ciò avvenga un cliente terminerebbe senza aver completato il taglio. 3 BARBIERE EQUO! Assegna un numero ad ogni cliente usando un contatore; Serializza l'accesso al contatore tramite il semaforo mutex1 ed il semaforo finito del caso precedente è ora sostituito da un array finito[numcli], il semaforo mutex2 si occupa invece della serializzazione del barbiere al cliente mentre che la varibile locale cliente_b fa corrispondere il numero del cliente al barbiere restituisca come numcli del wait(finito[numcli]). Il problema del barbiere ingiusto viene risolto introducendo ulteriori variabili e semafori. Ad ogni cliente che entra nel negozio viene associato un numero diverso dato dalla variabile intera contatore il cui accesso è protetto dal semaforo mutex1. Il semaforo finito viene sostituito da un array di 50 semafori per cui quando un cliente è seduto su una sedia esegue wait(finito[numcli]) con cui aspetta sul proprio semaforo e quando un barbiere finisce esegue signal(finito[numcli]) per liberare il giusto cliente. Il semaforo mutex2 è utilizzato invece per il coordinamento tra il barbiere e il cliente. E’ necessario dire che il barbiere conosce il numero del cliente grazie all’utilizzo della coda incoda1 in cui ogni cliente inserisce il proprio numero subito prima di attivare il semaforo cliente_pronto. Quando un barbiere è pronto per il taglio decoda1(cliente_b) toglie un numero in cima alla coda e lo mette nella variabile locale del barbiere cliente_b. ALGORITMO DEL BARBIERE EQUO program barbiere2; var capacita_max: semaforo (:=20); divano: semaforo (:=4); sedia_barbiere, coord: semaforo (:=3); mutex1, mutex2: semaforo (:=1); cliente_pronto, lascia_sedia_b, pagamento, ricevuta: semaforo (:=0); finito: array [1..50] of semaforo (:=0); contatore: integer; procedure cliente; var numcli: integer; begin wait(capacita_max); entra nel negozio; wait(mutex1); contatore:=contatore+1; numcli:=contatore; signal(mutex1); wait(divano); siedi sul divano; wait(sedia_barbiere); alzati dal divano; signal(divano); siedi sulla sedia del barbiere; wait(mutex2); incoda1(numcli); signal(cliente_pronto); signal(mutex2); wait(finito[numcli]); lascia la sedia del barbiere; signal(lascia_sedia_b); paga; signal(pagamento); wait(ricevuta); esci dal negozio; signal (capacita_max); end; procedure barbiere; var cliente_b:integer; begin repeat La slide presenta l’algoritmo del barbiere equo in cui è possibile osservare la definizione ed inizializzazione dei nuovi semafori mutex1 e mutex2, l’array finito dei 50 semafori e il contatore intero dei clienti. All’interno della programma principale il contatore dei clienti è inizializzato a zero. Nella procedura cliente quando il cliente entra nel negozio viene eseguita una wait(mutex1) che permette di serializzare l’accesso al contatore che viene incrementato di una unità, dunque alla variabile locale numcli che indica il numero del cliente viene associato il valore del contatore. Dopo di che viene effettuata la primitiva signal(mutex1) con cui si avvisa che il semaforo è libero. Quando il cliente si siede sulla sedia del barbiere viene emessa la primitiva wait(mutex2) che serializza l’accesso al barbiere, dunque il numero del cliente è inserito in una coda con l’istruzione incoda(numcli). Al termine del taglio quando il cliente è pronto viene eseguita un signal(mutex2) e una wait(finito[numcli]). Nella procedura barbiere quando il cliente è pronto per il taglio viene eseguita la primitiva wait sul semaforo mutex2 e l’istruzione decoda1(cliente_b) per estrarre il numero del cliente dalla coda, a questo punto avviene una signal(mutex2) e una volta terminato il taglio si segnala la 4 wait(cliente_pronto); wait(mutex2); decoda1(cliente_b); signal(mutex2); wait(coord); taglia i capelli; signal(coord); signal(finito[client_b]); wait(lascia_sedia_b); signal(sedia_barbiere); forever liberazione del cliente signal(finito[cliente_b]). con end; procedure cassiere; begin repeat wait(pagamento); wait(coord); prendi il denaro; signal(coord); signal(ricevuta); forever end; begin (*programma principale*) contatore:=0; parbegin cliente; …50 volte; …cliente; barbiere; barbiere; barbiere; cassiere; parend; end. 5