Ładowanie cegieł

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.

Rozwiązanie

Odwracanie kolejki czekających

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).

Bez odwracania kolejki

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;