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