Dyskoteka

W pewnej dyskotece bawią się procesy. Jednocześnie na parkiecie może przebyać co najwyżej K>1 procesów. Każdy proces przychodząc na dyskotekę czeka na dowolny proces odmiennej płci, po czym oba procesy czekaja na wolne miejsca na parkiecie, po czym razem idą tańczyć. Każdy z nich kończy taniec niezależnie od drugiego. W dyskotece pracuje też DJ, który dba o to, aby muzyka grała wtedy i tylko wtedy, gdy parkiet jest niepusty (tak, to oznacza, że muzyka może nigdy nie zamilknąć).

process Proces(nr: integer; plec: 0..1)
var
  partner: integer;
begin
  while (true) do
  begin
    DYSKOTEKA.CzekajNaPartneraIMiejsce(nr, plec, partner);
    tancz();
    DYSKOTEKA.Koncz();
  end;
end;

process DJ
begin
  while (true) do
  begin
    DYSKOTEKA.CzekajNaNiepusty;
    wlaczmuzyke;
    DYSKOTEKA.CzekajNaPusty;
    wylaczmuzyke;
  end;
end;

Napisz monitor DYSKOTEKA. Obowiązuje semantyka monitora z ćwiczeń.

Rozwiązanie:

Najpierw można się zastanowić nad rozwiązaniem prostszego problemu: procesy przychodzą same na dyskotekę i same tańczą.

monitor DYSKOTEKA;

czeka_dj: condition;
gra_dj: condition;
czeka_proces: condition;
muzyka_gra: boolean := false;
liczba_aktywnych: integer := 0;

procedure Czekaj_na_dj_i_miejsce()
begin
  if (not muzyka_gra or liczba_aktywnych=K) then
  begin
    if (not czeka_dj.empty()) then
      signal(czeka_dj);
    wait(czeka_proces);
  end;
  liczba_aktywnych++;
  if (liczba_aktywnych<K) then
    signal(czeka_proces);
end;

procedure Koncz()
begin
  liczba_aktywnych--;
  if (not empty(czeka_proces))
    signal(czeka_proces);
  else if (liczba_aktywnych=0) then
    signal(gra_dj);
end;

procedure CzekajNaNiepusty
begin
  if (empty(czeka_proces)) then
    wait(czeka_dj);
end;

procedure CzekajNaPusty
begin
  muzyka_gra := true;
  signal(czeka_proces); {:parkiet musi byc pusty, gdyz nie grala muzyka, wiec mozna kogos wpuscic:}
  wait(gra_dj);  
  muzyka_gra := false;
end;

Teraz zajmijmy się ustawieniem procesów w pary. Zapomnijmy na razie o procesie DJ i muzyce a zajmijmy się ustawieniem procesów w pary, i tym, żeby na parkiecie nie było jednocześnie więcej niż K procesów. W monitorze procesy czekające na zmiennych condition czekają w kolejkach prostych, to rozwiązanie korzysta z tego faktu.

liczba_m: integer := 0;
liczba_k: integer := 0;
czeka_pierwszy: condition;
czeka_drugi: condition;
id_1: integer;
id_2: integer;
liczba_aktywnych: integer := 0;

procedure Czekaj_na_miejsce()
begin
   if (K-liczba_aktywnych<2) then
     wait(czeka_drugi);
end;

procedure Czekaj(nr: integer, plec: integer, partner: integer)
var
 pierwszy: bool;
begin
 if (plac=M) then
 begin
  liczba_m++;
  if (liczba_m>liczba_k) then
    pierwszy := true;
  else
    pierwszy := false;
 end;
 else
 begin
  liczba_k++;
  if (liczba_m<liczba_k) then
    pierwszy := true;
  else
    pierwszy := false;
 end;

 if (pierwszy) then
   wait(czeka_pierwszy);
   partner := id_1;
   id_2 := nr;
   liczba_aktywnych++;
 else
   Czekaj_na_miejsce();
   id_1 := nr;
   signal(czeka_pierwszy);
   partner := id_2;
   liczba_aktywnych++;
end; 

procedure Koncz()
begin
  liczba_aktywnych--;
  if (K-liczba_aktywnych>1) then
     signal(czeka_drugi);
end;

Łącząc rozwiązania podproblemów, można rozwiązać problem przedstawiony w zadaniu.

liczba_m: integer := 0;
liczba_k: integer := 0;
czeka_pierwszy: condition;
czeka_proces: condition;
id_1: integer;
id_2: integer;
liczba_aktywnych: integer := 0;

procedure Czekaj_na_dj_i_miejsce()
begin
  if (not muzyka_gra or K-liczba_aktywnych<2) then
  begin
    if (not czeka_dj.empty()) then
      signal(czeka_dj);
    wait(czeka_proces);
  end;
end;

procedure Czekaj(nr: integer, plec: integer, partner: integer)
var
 pierwszy: bool;
begin
 if (plac=M) then
 begin
  liczba_m++;
  if (liczba_m>liczba_k) then
    pierwszy := true;
  else
    pierwszy := false;
 end;
 else
 begin
  liczba_k++;
  if (liczba_m<liczba_k) then
    pierwszy := true;
  else
    pierwszy := false;
 end;
 if (pierwszy) then
   wait(czeka_pierwszy);
   partner := id_1;
   id_2 := nr;
   liczba_aktywnych++;
 else
   Czekaj_na_dj_i_miejsce();
   id_1 := nr;
   signal(czeka_pierwszy);
   partner := id_2;
   liczba_aktywnych++;
   if (K-liczba_aktywnych>1) then
     signal(czeka_proces);
end; 

procedure Koncz()
begin
  liczba_aktywnych--;
  if (not empty(czeka_proces) and K-liczba_aktywnych>1) then
    signal(czeka_proces);
  else if (liczba_aktywnych=0) then
    signal(gra_dj);
end;