Zadanie 1 (Biuro)
W pewnym biurze grupa urzędników obsługuje grupę klientów. Każdy urzędnik ma unikatowy identyfikator z zakresu od 1 do N. Algorytmy urzędników i klientów są następujące:
process Urzędnik (u:integer, r:1..2) process Klient begin var u1,u2:integer; repeat begin BIURO.CHCĘ_PRACOWAĆ(u,r); repeat rozmawiam_z_klientem; BIURO.CHCĘ_ZAŁATWIĆ_SPRAWĘ(u1,u2); BIURO.SKOŃCZYŁEM; rozmawiam_z_urządnikiem(u1,u2); odpoczywam; BIURO.ZAŁATWILEM; until false; własne_sprawy; end; until false; end;
Rozwiązanie
monitor BIURO var ile_krzeseł: integer; ilu_u2: integer; { ilu jest gotowych urzędników II } u: array [1..2] of condition; { tu czekają urzędnicy I i II } k: condition; { tu czekają klienci } z_kim1, z_kim2: integer; { identyfikatory urzędników } export procedure CHCĘ_PRACOWAĆ(ranga: 1..2, id: integer); begin if ranga = 1 then begin { czeka, jeżeli nie ma klientów lub wystarczającej liczby krzeseł } if empty(k) or ile_krzeseł < 2 then wait u[1]; ile_krzeseł := ile_krzeseł - 2; z_kim1 := id; { zapisuje swój identyfikator } z_kim2 := 0; { informuje, ze nie będzie drugiego urzędnika } signal(k); { zwalnia klienta } end else begin { czeka, jeżeli nie ma klientów lub wystarczającej liczby krzeseł lub oczekującego urzędnika II } if empty(k) or ile_krzeseł < 3 or empty(u[2]) then begin inc(ilu_u2) wait u[2]; dec(ilu_u2); end; { jeżeli jest pierwszym urzędnikiem, to zapisuje swój identyfikator i budzi drugiego urzędnika } if z_kim1 = 0 then begin ile_krzeseł := ile_krzeseł - 3; z_kim1 := id; signal(u[2]); end { jeżeli jest drugim urzędnikiem, to zapisuje swój identyfikator i budzi klienta } else begin z_kim2 := id; signal(k); end; end; export procedure CHCĘ_ZAłATWIĆ_SPRAWĘ(var id1, id2: integer); begin { klient czeka jeżeli } if (empty(u[1]) or ile_krzeseł < 2) and { nie ma urzędnika I lub 2 krzeseł } (ilu_u2 < 2 or ile_krzeseł < 3) { nie ma dwóch urzędników II lub 3 krzeseł } then wait(k) else if not empty(u[1]) then signal(u[1]) { budzi urzędnika I } else signal(u[2]); { budzi pierwszego urzędnika II } id1 := z_kim1; { odczytuje identyfikatory } id2 := z_kim2; z_kim1 := 0; end; export procedure SKOŃCZYŁEM/ZAŁATWIŁEM; begin inc(ile_krzeseł); { jeżeli może dojść do rozmowy, to zwalnia odpowiedniego urzędnika } if not empty(k) then if not empty(u[1]) and ile_krzeseł >= 2 then signal(u[1]) else if ilu_u2 >= 2 and ile_krzeseł >= 3 then signal(u[2]); end; begin ile_krzeseł := K; ilu_u2 := 0; z_kim1 := 0; end.
Uwagi
Schemat rozwiązania jest następujący: kiedy nie może dojść do rozmowy (z powodu braku jednej ze stron lub miejsc do siedzenia), wtedy proces czeka w odpowiedniej kolejce. Jeżeli pojawi się brakujący urzędnik pierwszej rangi, to zwalnia klienta. Jeżeli pojawi się brakujący urzędnik drugiej rangi, to zwalnia drugiego urzędnika, który zwalnia klienta. Jeżeli pojawi się brakujący klient, to zwalnia urzędnika i jeżeli jest to urzędnik drugiej rangi, to zwalnia swojego kolegę, który zwolniłby klienta, ale tego nie robi, bo kolejka klientów jest pusta (gdyby nie była, to nie brakowało by klienta - patrz: początek zdania). Jeżeli pojawiają się brakujące krzesła, to zwalniani są odpowiedni urzędnicy, którzy zwalniają klienta.
Warto zwrócić uwagę na przekazywanie identyfikatorów klientowi. Jeżeli to klient zwalnia urzędników, to identyfikatory zostają przekazane w momencie, kiedy klient wykonuje signal
.
Priorytetowym wymaganiem w tym zadaniu jest obsłużenie jak największej ilości klientów. Aby je spełnić należy w pierwszej kolejności wybierać urzędników pierwszej rangi, ponieważ zajmują oni mniej krzeseł. Oczywiście może dojść do zagłodzenia drugiej grupy urzędników, ale jest to zagłodzenie wynikające z treści zadania.
Zadanie 2 (Zasoby)
W systemie są dwie grupy procesów korzystające z N zasobów typu A i M zasobów typu B (N + M > 1). Procesy z pierwszej grupy cyklicznie wykonują własne sprawy, po czym wywołują procedurę zamieńAB
, która konsumuje jeden zasób A i produkuje jeden zasób B. Procesy z grupy drugiej cyklicznie wykonują własne sprawy, po czym wywołują procedurę zamień
, która konsumuje jeden zasób dowolnego typu i produkuje zasób przeciwny. Zsynchronizuj procesy za pomocą monitora tak, aby:
zamieńAB
i zamień
były wywoływane przez procesy jedynie pod warunkiem dostępności odpowiednich zasobówzamieńAB
parami, tzn. proces z grupy pierwszej może rozpocząć jej wykonanie jedynie wtedy, gdy jest inny proces z grupy pierwszej, gotowy do jej wykonania (i oczywiście niezbędne zasoby)Monitor powinien udostępniać jedynie procedury wywoływane przez procesy przed i po rozpoczęciu korzystania z zasobów.
Rozwiązanie
monitor Zasoby; var ileA, ileB, iluNaA: Integer; zasóbA, zasób, partner: condition; export procedure ChceA; begin inc(iluNaA); if iluNaA mod 2 = 1 then { pierwszy z pary } wait (partner); { czeka na partnera } else begin { drugi z pary } if ileA < 2 then { jeżeli nie ma dwóch zasobów A } wait (zasóbA); { czeka na co najmniej dwa zasoby A } ileA := ileA - 2; signal (partner) { zwalnia partnera } end dec(iluNaA); end; export procedure ChceZasób (var z: Zasob); begin if (ileB = 0) and (ileA = 0 or not empty(zasóbA)) then wait (zasób); { czeka na cokolwiek } if ileB > 0 then { najpierw B, żeby produkować A } begin z := B; dec (ileB) end else begin z := A; dec (ileA) end end; export procedure Wyprodukowalem (z: Zasób); begin if z = B then { jeżeli wyprodukowano B } begin inc (ileB); signal (Zasób) end else begin inc (ileA); if empty (ZasóbA) then { tylko jeżeli nie ma par w pierwszej grupie } signal (Zasób) { to zwalniamy swoją grupę } else { czeka para na zasób A } if ileA > 1 then { zwalniamy ja jeśli można lub } signal (ZasóbA) { czekamy na drugi egzemplarz zasobu A } end end; begin iluNaA := 0; ileA := N; ileB := M; end.
Uwagi
Aby nie zagłodzić procesów pierwszej grupy, proces który wyprodukował A, zwalnia procesy drugiej grupy tylko wtedy, gdy nie ma pary gotowych procesów z grupy pierwszej, nawet jeżeli jest tylko jeden zasób, który na razie jest dla tej pary bezużyteczny.