Grupa N (N>0) robotników ma załadować stertę cegieł na samochód. Każdy z nich musi wiedzieć od kogo ma odbierać cegły i komu podawać, czyli musi znać numery obu swoich sąsiadów (należy przyjąć, że sterta cegieł ma numer 0, a samochód N+1). Pracę można rozpocząć dopiero wtedy, gdy zgłoszą się wszyscy robotnicy. Po zakończeniu załadunku robotnicy przychodzą po wypłatę. Każdy podaje swoją stawkę za pracę, na podstawie której wylicza się stawkę średnią, którą otrzymują wszyscy. Każdy robotnik działa według schematu:
process Robotnik(i: integer); const moja_stawka = ...; var lewy,prawy,wyplata: integer; begin while (true) do begin M.chce_pracowac(i, lewy, prawy); podawaj_cegly(lewy, prawy); M.wyplata(moja_stawka, wyplata); odpoczynek(wyplata); end; done end;
Należy napisać monitor M. Nie wolno deklarować żadnych struktur rozmiaru N lub większego.
monitor M; var czekanie_na_grupe: condition; ostatni_lewy: integer := 0; ostatni_prawy: integer; ilu_czeka_na_grupe: integer := 0; suma: integer := 0; stawka: integer; export procedure chce_pracowac(in p_i: integer, out p_lewy: integer, out p_prawy: integer) begin p_lewy := ostatni_lewy; if (ilu_czeka_na_grupe<N-1) begin ostatni_lewy := p_i; ilu_czeka_na_grupe++; wait(czekanie_na_grupe); ilu_czeka_na_grupe--; signal(czekanie_na_grupe); p_prawy := ostatni_prawy; ostatni_prawy := p_i; end; else begin p_prawy := N+1; ostatni_prawy := p_i; signal(czekanie_na_grupe); ostatni_lewy := 0; end; end; export procedure wyplata(in p_stawka: integer, out p_wyplata: integer) begin suma := suma + p_stawka; if (ilu_czeka_na_grupe<N-1) begin ilu_czeka_na_grupe++; wait(czekanie_na_grupe); ilu_czeka_na_grupe--; end; else begin stawka := suma div N; end; p_wyplata := stawka; if (empty(czekanie_na_grupe)) suma := 0; else signal(czekanie_na_grupe); end;
Przedstawione rozwiązanie odwraca kolejkę procesów czekających na grupę. Procesy czekające wykonują:
wait
aktualizacja pierwsza
signal
aktualizacja druga
Zauważmy, że aktualizacja pierwsza jest wykonywana po obudzeniu, ale przed wyjściem na zewnątrz monitora (operacja signal) - operację wykonują procesy w kolejności takiej jak czekały na zmiennej czekanie_na_grupę (a więc w takiej w jakiej przychodziły do monitora). Istotne jest także wykonanie części aktualizacja druga - ta część wykonuje się gdy procesy wracają do monitora (wykonuje się ona w kolejności odwrotnej do tej w jakiej procesy czekały na zmiennej czekanie_na_grupę - gdyż proces wykonujący signal ustawia się na początku kolejki wejściowej do monitora).
W tej części przedstawione jest inne rozwiązanie, które nie odwraca kolejki. Zauważmy, że robotnik nie musi czekać aż przyjdą wszyscy robotnicy, żeby znał lewego i prawego robotnika. Gdy robotnicy są przetwarzani sekwencyjnie, to wystarczy, żeby pamiętać identyfikator poprzedniego (starego) robotnika i poczekać aż przyjdzie następny i zapisze swój identyfikator. Zadanie wymaga, aby robotnicy rozpoczeli pracę, gdy zbiorą się wszyscy. Przedstawiona jest tutaj procedura chce_pracowac_ustawianie, która wyłącznie ustawia pracowników w kolejności, synchronizację przed pracę trzeba dodać aby spełnić wymagania zadania. Synchronizacja ta nie jest trudna, dlatego nie została tu zamieszczona.
var aktualny: condition; liczba_ustawianych: integer := 0; stary : integer := 0; nowy : integer; procedure chce_pracowac_ustawianie(in p_i: integer, out p_lewy: integer, out p_prawy: integer) begin inc(liczba_ustawianych); if (liczba_ustawianych<>1) then {: jesli robotnik ma poprzednika, to ustawia informacje o sobie, a nastepnie budzi poprzednika :} begin nowy := p_i; signal(aktualny); end; if (liczba_ustawianych<>N) then {: robotnik nie jest ostatni :} begin wait(aktualny); p_lewy := stary; p_prawy := nowy; stary := p_i; end; else {: robotnik jest ostatni, to nie czeka az nastepny go obudzi :} begin p_lewy := stary; p_prawy := N+1; stary := 0; liczba_ustawianych := 0; end; end;