Pewien zasób jest dzielony między dwie grupy procesów:
procesy niedokonujące zmian w zasobie
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.
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.