Cz ± I Rozwi zania klasycznych problemów w Rendezvous 1 Producenci i konsumenci Na pocz tek rozwa»my wersj z jednym producentem i jednym konsumentem, dziaªaj cymi w niesko«czonych p tlach. Mechanizm komunikacji synchronicznej umo»liwia rozwi zanie problemu producenta i konsumenta bez jakiegokolwiek bufora. Producent przekazuje wyprodukowan porcj bezpo±rednio do konsumenta; dopiero po jej odebraniu mo»e wyprodukowa nast pn. type p o r c j a ; void produkuj ( p o r c j a &p ) ; void konsumuj ( p o r cja p ) ; send Konsument. p ; Je±li Producent produkuje tylko sko«czon liczb porcji, nieznan Konsumentowi, poprawne zako«czenie obu procesów wymaga wprowadzenia sygnaªu koniec(). bool d a l e j = true ; while ( d a l e j ) select { on p on koniec ( ) d a l e j = f a l s e ; 1
for ( i in 0.. N 1) { send Konsument. p ; send Konsument. koniec ( ) ; Teraz mo»emy zmodykowa nasz problem zast puj c jednego konsumenta przez K konsumentów. Zastosowanie mechanizmu komunikacji synchronicznej jest trudniejsze ni» w przypadku komunikacji asynchronicznej. Producent przed wysªaniem porcji musi wiedzie, do którego konsumenta mo»e j przekaza. Jest to niezb dne po to, aby nie próbowaª wysªa porcji do zaj tego konsumenta, podczas gdy inny oczekiwaªby na ni. Dlatego wprowadzimy dodatkowy sygnaª chc (), który proces Konsument wysyªa wtedy, gdy chce otrzyma kolejn porcj. select for ( i in 1..K) // n i e d e t e r m i n i s t y c z n y on Konsument [ i ]. chc ( ) // wybó r send Konsument [ i ]. p ; // gotowego konsumenta process Konsument [ i : 1..K] { send Producent. chc ( ) ; // Wersja 2 : p r z e s y ª anie pid ów receive chc ( i ) ; send Konsument [ i ]. p ; 2
process Konsument [ i : 1..K] { send Producent. chc ( i ) ; Teraz rozwa»my wersj problemu z jednym producentem, jednym konsumentem i jednoelementowym buforem. Poniewa» w Rendezvous procesy nie mog komunikowa si przez wspóln pami, rozwi zanie z buforem wymaga wprowadzenia dodatkowego procesu Bufor komunikuj cego si bezpo±rednio z procesami Producent i Konsument. send Bufor. p ; process Bufor { send Konsument. p ; Zastanówmy si, co zmieni si w tym rozwi zaniu, je±li zamienimy bufor jednoelementowy na Melementowy cykliczny. Mo»e si wydawa,»e tre±ci procesów Producent i Konsument powinny pozosta bez zmian. Tak jednak by nie mo»e, poniewa» proces Bufor musi w jaki± sposób zdecydowa, kiedy wykona instrukcj wyj±cia, a kiedy wej±cia. Do podj cia takiej decyzji nie wystarczy sprawdzenie, czy bufor jest niepusty, bowiem wówczas proces Bufor mógªby oczekiwa na spotkanie z procesem Konsument wtedy, gdy tamten jest zaj ty 3
konsumowaniem. W tym samym czasie producent nie mógªby nic wªo»y do bufora. Zmniejszyªoby to wykorzystanie bufora i niepotrzebnie wstrzymywaªo producenta. Dlatego nale»y wprowadzi sygnaª chc (), który proces Konsument wysyªa wtedy, gdy jest gotowy na pobranie nowej porcji z bufora. send Bufor. p ; send Bufor. chc ( ) ; process Bufor { p o r c j a bufor [M] ; int pozycja = 0 ; int i l e = 0 ; while ( true ) select { i f ( i l e < M) on bufor [ ( pozycja + i l e ) % M] ++i l e ; i f ( i l e > 0) on chc ( ) { send Konsument. bufor [ pozycja ++]; pozycja %= M; i l e ; Wszystkie poprzednie rozwi zania miaªy bufor scentralizowany lub nie miaªy go wcale. Teraz rozwa»my bufor rozproszony. Istnieje tutaj kilka wariantów organizacji bufora. Na pocz tek zajmijmy si buforem, w którym kolejno± wkªadania i wyjmowania nie jest istotna (np. przechowujemy materiaªy nie ulegaj ce przeterminowaniu). 4
select for ( i : 1.. M) on Bufor [ i ]. pusty ( ) // r e c e i v e pusty ( i ) ; send Bufor [ i ]. p ; // send Bufor [ i ]. p ; process Bufor [ i : 1..M] { send Producent. pusty ( ) ; send Konsument. p ; Powstaje naturalne pytanie, co zrobi aby porcje byªy pobierane w kolejno±ci wstawiania. W tym celu mo»emy inaczej zorganizowa prac bufora, producenta i konsumenta. Producent b dzie przekazywa porcje do pierwszego bufora, ten do drugiego, a ostatni bufor do konsumenta. W tej wersji Bufor musi zna swój numer w tablicy. send Bufor [ 1 ]. p ; process Bufor [ i : 1..M] { i f ( i == M) send Konsument. p ; 5
else send Bufor [ i + 1 ]. p ; Powy»sze rozwi zanie ma pewn wad. Jest ni du»y czas przesyªania produktu od producenta do konsumenta. Aby tego unikn mo»na wprowadzi przeª cznik wskazuj cy który bufor mamy zapeªni (opró»ni ) jako nast pny. int k o l e j n y = 1 ; send Bufor [ k o l e j n y ++].p ; k o l e j n y %= M; int k o l e j n y = 1 ; receive Bufor [ k o l e j n y ++].p ; k o l e j n y %= M; process Bufor [ i : 1..M] { send Konsument. p ; 6
2 Czytelnicy i pisarze z zamian ról W systemie dziaªa serwer synchronizuj cy prac procesów. Ka»dy proces cyklicznie zaªatwia wªasne sprawy, po czym wchodzi do sekcji krytycznej. Dost p do sekcji mo»e by wyª czny lub dzielony. Proces» daj cy wyª cznego dost pu do sekcji krytycznej mo»e w niej przebywa jako jedyny. Proces» daj cy dzielonego dost pu mo»e korzysta z sekcji krytycznej w tym samym czasie co inne procesy» daj ce dost pu dzielonego. Ponado proces b d c w sekcji krytycznej mo»e co najwy»ej raz za» da zmiany sposobu korzystania z sekcji. Procesy» - daj ce zmiany sposobu dost pu z dzielonego na wyª czny zostaj wstrzymane do momentu, w którym b d mogªy korzysta z sekcji na zasadzie wyª czno±ci. Procesy te maj pierwsze«stwo w dost pie do sekcji przed nowymi procesami. Napisz tre± procesu serwera i procesów» daj cych dost pu do sekcji krytycznej w sposób dzielony oraz wyª czny. #define C 10 #define P 10 void c z y t a n i e ( ) ; void p i s a n i e ( ) ; process Czytelnik [ i : 1..C] { send Czytelnia. chc Czyta ( i ) ; receive mo» eszczyta ( ) ; c z y t a n i e ; i f ( nie_zmieniam ) send Czytelnia. koniecczytania ( ) ; else { send Czytelnia. zmiana ( i ) ; receive mo» e s z P i s a ( ) ; p i s a n i e ; send Czytelnia. k o n i e c P i s a n i a ( ) ; process P i s a r z [ i : 1.. P] { send Czytelnia. chc Pisa ( ) ; receive mo» e s z P i s a ( ) ; p i s a n i e ( ) ; i f ( nie_zmieniam ) send Czytelnia. k o n i e c P i s a n i a ( ) ; else { send Czytelnia. zmiana ( ) ; 7
c z y t a n i e ; send Czytelnia. koniecczytania ( ) ; void CzekajNaPusta ( ) { // t y l k o gdy s c z y t e l n i c y int kto ; i f ( i l e _ c z y t a > 0) select { on koniecczytania ( ) { // koniec pracy c z y t e l n i k a ile_czyta ; CzekajNaPusta ( ) ; on zmiana ( kto ) { // zamiana c z y t e l n i k a w p i s a r z a ile_czyta ; CzekajNaPusta ( ) ; send Czytelnik [ kto ]. mo» e s z P i s a ( ) ; receive k o n i e c P i s a n i a ( ) ; void CzekajNaPusta ( ) { // wersja i t e r a c y j n a int kto, i ; int ktoczeka [C ] ; // kto czeka na zmian int i l e C z e k a = 0 ; // i l u czeka na zmian while ( i l e _ c z y t a > 0) select { on koniecczytania ( ) // koniec pracy c z y t e l n i k a ile_czyta ; on zmiana ( kto ) { // zmiana c z y t e l n i k a w p i s a r z a ile_czyta ; ktoczeka [ i l eczeka++] = kto ; for ( i = 0 ; i < i l e C zeka ; i++) { // wpuszczanie wg // k o l e j n o ± c i zg ª osze «send Czytelnik [ ktoczeka [ i ] ]. mo» e s z P i s a ( ) ; receive k o n i e c P i s a n i a ( ) ; i l e C z e k a = 0 ; 8
process Czytelnia { int c z y t a j cy = 0 ; int kto ; while ( true ) select { on chc Czyta ( kto ) { c z y t a j cy++; send Czytelnik [ kto ]. mo» eszczyta ( ) ; on koniecczytania ( ) c z y t a j cy ; // i f ( c z y t a j a c y > 0) on zmiana ( kto ) { // zmiana c z y t e l n i k a w p i s a r z a czytajacy ; CzekajNaPusta ( ) ; send Czytelnik [ kto ]. mo» e s z P i s a ( ) ; receive k o n i e c P i s a n i a ( ) ; on chc Pisa ( kto ) { CzekajNaPusta ( ) ; send P i s a r z [ kto ]. mo» e s z P i s a ( ) ; select { on k o n i e c P i s a n i a ( ) ; on P i s a r z [ kto ]. zmiana ( ) // zmiana p i s a r z a c z y t a j cy++; //... = 1 9
3 Czytelnicy i pisarze Mamy do dyspozycji czytelni posiadaj c nieograniczon liczb miejsc. Z czytelni chc korzysta czytelnicy i pisarze. W czytelni mo»e przebywa wielu czytelników naraz, jednak»e pisarz potrzebuje skupienia i kiedy pisze, to obecno± jakiejkolwiek innej osoby rozprasza go. Chcemy napisa rozwi zanie, które umo»liwiaªoby wchodzenie nowych czytelników do czytelników ju» przebywaj cych w czytelni. Zezwalamy tym samym na zagªodzenie pisarzy. Zakªadamy,»e mamy C czytelników i P pisarzy. #define C 10 #define P 10 void c z y t a n i e ( ) ; void p i s a n i e ( ) ; process Czytelnik [C] { send Czytelnia. czytam ( ) ; c z y t a n i e ( ) ; send Czytelnia. koniecczytania ( ) ; process P i s a r z [P] { send Czytelnia. p i s z ( ) ; p i s a n i e ( ) ; send Czytelnia. k o n i e c P i s a n i a ( ) ; process Czytelnia { int c z y t a j cy = 0 ; while ( true ) select { on czytam ( ) ++c z y t a j cy ; on koniecczytania ( ) c z y t a j cy ; i f ( c z y t a j cy == 0) on p i s z ( ) receive k o n i e c P i s a n i a ( ) ; Teraz napiszmy rozwi zanie poprawne, które uzyskujemy w ªatwy sposób z poprzedniego przez doªo»enie sygnaªu chc Pisa () do procesu Pisarz. 10
#define C 10 #define P 10 void c z y t a n i e ( ) ; void p i s a n i e ( ) ; process Czytelnik [ i : 1..C] { send Czytelnia. czytam ( ) ; c z y t a n i e ( ) ; send Czytelnia. koniecczytania ( ) ; process P i s a r z [ i : 1.. P] { send Czytelnia. chc Pisa ( ) ; receive mo» e s z P i s a ( ) ; p i s a n i e ( ) ; send Czytelnia. k o n i e c P i s a n i a ( ) ; process Czytelnia { int c z y t a j cy = 0 ; while ( true ) select { on czytam ( ) ++c z y t a j cy ; on koniecczytania ( ) c z y t a j cy ; for ( i : 1.. P) on P i s a r z [ i ]. chc Pisa ( ) { while ( c z y t a j cy > 0) { receive koniecczytania ( ) ; c z y t a j cy ; send P i s a r z [ i ]. mo» e s z P i s a ( ) ; receive k o n i e c P i s a n i a ( ) ; Subtelno± tego rozwi zania polega na wykorzystaniu»ywotno±ci niedeterminizmu wyst puj cego w Rendezvous. Je±li na wej±cie do czytelni czekaj zarówno pisarze jak i czytelnicy, to zostanie wpuszczony który± z tych procesów, ale nie mamy wpªywu na to który. Rozwi zanie nie doprowadzi do zagªodzenia 11
»adnej grupy dzi ki»ywotno±ci dozorów. Je±li chcieliby±my sami decydowa o tym, kogo wpuszczamy do czytelni i, na przykªad, po wyj±ciu pisarza wpuszcza wszystkich oczekuj cych czytelników, aby w peªni wykorzysta czytelni, to rozwi zanie skomplikuje si nieco. #define C 10 #define P 10 void c z y t a n i e ( ) ; void p i s a n i e ( ) ; process Czytelnik [ i : 1..C] { send Czytelnia. chc Czyta ( ) ; send Czytelnia. zaczynamczytanie ( ) ; c z y t a n i e ( ) ; send Czytelnia. koniecczytania ( ) ; process P i s a r z [ i : 1.. P] { send Czytelnia. chc Pisa ( ) ; send Czytelnia. zaczynampisanie ( ) ; p i s a n i e ( ) ; send Czytelnia. k o n i e c P i s a n i a ( ) ; process Czytelnia { int c z y t a j cy = 0 ; int c z e k a j cyczytelnicy = 0 ; int p i s z cy = 0 ; int c z e k a j c y P i s a r z e = 0 ; while ( true ) select { on chc Czyta ( ) i f ( p i s z cy + c z e k a j c y P i s a r z e == 0) { ++c z y t a j cy ; receive zaczynamczytanie ( ) ; else ++c z e k a j cyczytelnicy ; on koniecczytania ( ) i f ( c z y t a j cy == 0 && c z e k a j c y P i s a r z e > 0) { receive zaczynampisanie ( ) ; p i s z cy = 1 ; c z e k a j c y P i s a r z e ; 12
on chc Pisa ( ) i f ( c z y t a j cy + p i s z cy == 0) { p i s z cy = 1 ; receive zaczynampisanie ( ) ; else ++c z e k a j c y P i s a r z e ; on k o n i e c P i s a n i a ( ) { p i s z cy = 0 ; i f ( c z e k a j cyczytelnicy > 0) { while ( c z e k a j cyczytelnicy > 0) { receive zaczynamczytanie ( ) ; c z e k a j cyczytelnicy ; else i f ( c z e k a j c y P i s a r z e > 0) { receive zaczynampisanie ( ) ; p i s z cy = 1 ; c z e k a j c y P i s a r z e ; 13