Czytelnicy i pisarze

Pewien zasób jest dzielony między dwie grupy procesów:

czytelnicy

procesy niedokonujące zmian w zasobie

pisarze

pozostałe procesy

Z zasobu może korzystać jednocześnie wielu czytelników. Pisarz może korzystać z zasobu tylko w sposób wyłączny.

type:
  komunikat_typ: (ChceCzytelnik, ChcePisarz, MozeCzytelnik, MozePisarz, ZwalniaCzytelnik, ZwalniaPisarz);

const:
  LICZBA_CZYTELNIKOW = ...;
  LICZBA_PISARZY = ...;

var:
  operacje: buffer;
  moze_czytelnik: buffer;
  moze_pisarz: buffer;
  i: integer;

process Czytelnik(i: integer)
var:
  komunikat_typ k;
begin
  while (true) do
  begin
    SendMessage(operacje, ChceCzytelnik);
    GetMessage(moze_czytelnik, k); {k=MozeCzytelnik}
    czytanie();
    SendMessage(operacje, ZwalniaCzytelnik);
  end;
end;

process Pisarz(i: integer)
var:
  komunikat_typ k;
begin
  while (true) do
  begin
    SendMessage(operacje, ChcePisarz);
    GetMessage(moze_pisarz, k); {k=MozePisarz}
    pisanie();
    SendMessage(operacje, ZwalniaPisarz);
  end;
end;

process Czytelnia
var:
  ...
begin
  ...
end;

begin
  cobegin
  for i:=1 to LICZBA_CZYTELNIKOW do
    begin
      Czytelnik(i);
    end;
  for i:=1 to LICZBA_PISARZY do
    begin
      Pisarz(i);
    end;
  Czytelnia();
  coend
end;

Dopisz proces Czytelnia do tego systemu.

Metoda Pojedynczego Przetwarzania Żądań

Możemy zastosować metodę Pojedynczego Przetwarzania Żądań. Gdy pobieramy żądanie, to zajmujemy się nim dopóki go nie zrealizujemy (nie zabieramy się za przetwarzanie innych żądań dopóki nie skończy aktualnego). W tym celu musimy rozdzielić bufor żądań od bufora zwalniania zasobów (czasami gdy chcemy zrealizować pewne żądanie to musimy poczekać na zwolnienie zasobów).

type:
  komunikat_typ: (ChceCzytelnik, ChcePisarz, MozeCzytelnik, MozePisarz, ZwalniaCzytelnik, ZwalniaPisarz);

var:
  zwalnianie: buffer;

process Czytelnik(i: integer)
var:
  komunikat_typ k;
begin
  while (true) do
  begin
    SendMessage(operacje, ChceCzytelnik);
    GetMessage(moze_czytelnik, k); {k=MozeCzytelnik}
    czytanie();
    SendMessage(zwalnianie, ZwalniaCzytelnik);
  end;
end;

process Pisarz(i: integer)
var:
  komunikat_typ k;
begin
  while (true) do
  begin
    SendMessage(operacje, ChcePisarz);
    GetMessage(moze_pisarz, k); {k=MozePisarz}
    pisanie();
    SendMessage(zwalnianie, ZwalniaPisarz);
  end;
end;

process Czytelnia
var:
  k: komunikat_typ;
  ilu_czytelnikow_czyta: integer;
  ilu_pisarzy_pisze: integer;
begin
  while (true) do
  begin
    k := GetMessage(operacje);
    case k of
      ChceCzytelnik:
        do
          begin
            ilu_czytelnikow_czyta := ilu_czytelnikow_czyta + 1;
            SendMessage(moze_czytelnik, MozeCzytelnik);
            k := GetMessage(operacje);
          end;
        while (k<>ChcePisarz);
        {k=ChcePisarz}
        while (ilu_czytelnikow_czyta>0) do
          begin
            k := GetMessage(zwalnianie); 
            {k=ZwalniaCzytelnik}
            ilu_czytelnikow_czyta := ilu_czytelnikow_czyta - 1;
          end;
        ilu_pisarzy_pisze := ilu_pisarzy_pisze + 1;
        SendMessage(moze_pisarz, MozePisarz);
        k := GetMessage(zwalnianie);
        {k=ZwalniaPisarz}
        ilu_pisarzy_pisze := ilu_pisarzy_pisze - 1; 
      ChcePisarz:
        ilu_pisarzy_pisze := ilu_pisarzy_pisze + 1;
        SendMessage(moze_pisarz, MozePisarz);
        k := GetMessage(zwalnianie);
        {k=ZwalniaPisarz}
        ilu_pisarzy_pisze := ilu_pisarzy_pisze - 1;
    end; {case}
  end;
end;

Rozwiązanie ma pewien mały mankament. Gdy działają sami czytelnicy i cały czas korzystają z czytelni (następni wchodzą, zanim wcześniejsi opuszczą czytelnię), to proces nie odbiera komunikatów o zwalnianiu czytelni, to prowadzi do tego, że tych komunikatów w tej sytuacji nazbiera się dowolnie dużo (zależnie od długości działania czytelników w ten sposób). Teoretycznie (przy założeniu że bufory są nieograniczone) nie stanowi to problemu, w praktyce jednak często jest inaczej.