WYKŁAD 4 SEMAPHORES (CIĄG DALSZY) Przykład 6 Problem czytelników i pisarzy Wykład 4 strona 1/24
Ilustracja 5. Schemat problemu czytelników i pisarzy Wykład 4 strona 2/24
Opis problemu: Jest n czytelników, m pisarzy i jedna wspólna strona. KaŜdy czytelnik czyta informacje ze strony, kaŝdy pisarz moŝe pisać na stronie; Wielu czytelników moŝe naraz czytać dane ze strony; Pisarz zajmuje stronę na wyłączność (Ŝaden inny proces pisarz czy czytelnik nie moŝe uŝywać w tym czasie strony); Nie ma ograniczeń czasowych na czytanie i pisanie, ale operacje te są skończone; Rozwiązanie: Pisarz: Robi coś; Chce pisać; Pisze; Informuje, Ŝe skończył pisać; Czytelnik: Robi coś; Chce czytać; Czyta; Informuje, Ŝe skończył czytać; Wykład 4 strona 3/24
int no_of_r = 0; /* liczba procesów - czytelników aktualnie czytających*/ Boolean semaphore sp, w = 1, 1 ; /* w słuŝy do synchronizacji dostępu do no_of_r, sp słuŝy do synchronizacji dostępu do strony */ process :: reader; void main() while(1) PB(w); no_of_r ++; if (no_of_ r == 1) PB(sp); /* pierwszy czytający proces opuszcza sp */ VB(w); /* czyta ze strony*/ PB(w); no_of_r --; if (no_of_r == 0) VB(sp); /* ostatni czytający proces podnosi semafor sp */ VB(w); Wykład 4 strona 4/24
process :: writer; void main() while(1) PB(sp); /* kaŝdy piszący proces musi opuścić semafor sp*/ /* pisze na stronie */ VB(sp); Wykład 4 strona 5/24
Przykład 6a Problem czytelników i pisarzy z priorytetem dla pisarzy. Dodajemy dwa dodatkowe warunki do zadania: Pisarze mają priorytet, czyli, jeśli jakikolwiek pisarz chce pisać, Ŝaden czytelnik nie moŝe zacząć czytać. Nie ma priorytetu pomiędzy czytelnikami. int no_of_r,no_of_w = 0,0; /*no_of_r - liczba aktualnie czytających procesów no_of_w liczba procesów, które chcą pisać */ Boolean semaphore sp,sr = 1, 1; Boolean semaphore w1, w2, w3= 1, 1, 1; /* w1 do dostępu do no_of_r w2 do dostępu do no_of_w w3 dodatkowe drzwi dla czytelników sp podobnie jak w poprzednim przykładzie sr dla priorytetu pisarzy*/ Wykład 4 strona 6/24
process:: reader; void main() while(1) PB(w3); PB(sr); PB(w1); no_of_r++; if (no_of_r == 1) VB(w1); VB(sr); VB(w3); /* czyta */ PB(w1); no_of_r--; if ( no_of_r == 0) VB(w1); PB(sp); VB(sp); Wykład 4 strona 7/24
process :: writer; void main(); while(1) PB(w2); no_of_w++; if (no_of_w == 1 ) PB(sr); VB(w2); PB(sp); /* pisze */ VB(sp); PB(w2); no_of_w--; if (no_of_w==0) VB(sr); VB(w2); Wykład 4 strona 8/24
Jednoczesne operacje na semaforach Modyfikacje semaforów Rozszerzone operacje na semaforach (ang. Concurrent semaphore operations) (Dijkstra) PD(s 1,s 2,...,s i,...,s n ); Czekaj aŝ wszystkie si, si>0 (i=1,...,n); for (i=1;i<=n;i++) si = si-1; VD(s 1,s 2,...,s i,...,s n ); for (i=1;i<=n;i++) si = si+1; Wykład 4 strona 9/24
Uogólnione operacje na semaforach Wartość semafora jest zmieniana o wartość całkowitą n. PN(s,n); Waiting until s >= n; s=s-n; VN(s,n); s = s+n; Wykład 4 strona 10/24
Priorytety w dostępie do zasobów Priorytety są realizowane na podstawie numeru procesu. Procesy z mniejszymi numerami mają wyŝszy priorytet. Wykład 4 strona 11/24
semaphore prior = 2*N-1; // N liczba procesów process:: P(name) /*1 <=name<=n */ int i; while(1) PN(prior,N+name-1); /* prior == N-name Ŝaden inny proces nie moŝe wejść do sekcji krytycznej */ /*zaŝądaj zasobu; */ /*sekcja krytyczna */ /*zwolnienie zasobu */ VN(prior, name-1); /* prior = =N-1 nadal Ŝaden proces nie moŝe wejść do sekcji krytycznej*/ for(i=1;i<=n;i++) /* wyszukiwanie czekającego procesu z najwyŝszym priorytetem */ VN(prior,1); /* prior == N+i-1 proces z numerem i moŝe wejść do sekcji krytycznej, i=1,...,n */ } } Wykład 4 strona 12/24
Przykład 6 (zmieniony) Czytelnicy i pisarze bez priorytetu semaphore w = M; /* M > liczba procesów - pisarzy*/ process :: reader; while(1) PN(w,1); /*M moŝe zwiększyć w o 1 */ /* teraz czyta */ VN(w,1); process :: writer; while(1) PN(w,M); /*tylko jeden proces moŝe zmniejszyć w o M*/ /* teraz pisze*/ VN(w,M); Wykład 4 strona 13/24
Przykład 6 a (zmieniony) Czytelnicy - pisarze z priorytetem dla pisarzy semaphore sp, r = M,M ; /* M>= liczby moŝliwych czytelników sp podobnie jak we wcześniejszym przykładzie r dla realizowania priorytetu pisarzy*/ Wykład 4 strona 14/24
process :: reader; void main(); while(1) PN(r,M); PN(sp,1); VN(r,M-1); /*pisarze mogą zwiększyć semafor r o 1*/ VN(r,1); /*czytelnik czeka jeśli jakiś pisarz czeka*/ /* czyta*/ VN(sp,1); process :: writer; void main(); while(1) PN(r,1); PN(sp,M); /* pisze */ VN(sp,M); VN(r,1); Wykład 4 strona 15/24
Przykład 7. n filozofów je rybę. Ilustracja 7.1 Filozofowie jedzący rybę Wykład 4 strona 16/24
Opis problemu: N filozofów siedzi dokoła okrągłego stołu, kaŝdy na swoim miejscu. Jest n talerzy i n widelców na stole. Talerze znajdują się na wprost filozofów, widelce leŝą pomiędzy talerzami. Czynności filozofa: pętla Myśli Chce podnieść swoje widelce Je rybę posługując się widelcami Odkłada widelce Wykład 4 strona 17/24
Rozwiązanie problemu: Spróbuj podnieść swoje widelce Rozwiązanie 1. Czekaj aŝ lewy widelec będzie wolny Podnosi lewy widelec Czekaj aŝ prawy widelec będzie wolny Podnosi prawy widelec Złe rozwiązanie: zakleszczenie Wykład 4 strona 18/24
Rozwiązanie 2: Pętla Czekaj aŝ lewy jest wolny Podnieś lewy IF prawy jest zajęty OdłóŜ lewy widelec ELSE Podnieś prawy widelec EXIT } } Złe rozwiązanie: zakleszczenie Wykład 4 strona 19/24
Wniosek: Rozwiązanie 3: Czekaj aŝ oba będą wolne Podnieś oba widelce w jednym ruchu OdłóŜ widelce OdłóŜ widelce w dowolnej kolejności Obudź tego sąsiada, który czeka na widelec (jeśli jego drugi widelec jest wolny) Wykład 4 strona 20/24
Pierwsze rozwiązanie z semaforami - zakleszczenie resource fork [N]; Boolean semaphore sem [N] = (N) 1; process Philosopher (name); while(1) /* myśli */ /* chce jeść */ PB ( sem[name]); /* podnosi lewy widelec - fork[name] */ PB ( sem[(name+1) % N] ) ; /* podnosi prawy widelec - fork[(name+1) % N] */ /* je */ VB ( sem[name] ); /*odkłada fork[name],*/ VB ( sem[(name+1) % N] ) ; /* odkłada fork [(name+1) % N] */ Wykład 4 strona 21/24
2 rozwiązanie state[i] = 0 i-ty filozof myśli state[i] = 1 i-ty filozof chce jeść state[i] = 2 i-ty filozof je resource fork [N]; Boolean semaphore filsem [N] = (N) 0; /*kaŝdy filozof ma swój własny semafor do czekania*/ int state [N] = (N) 0 ; Boolean semaphore w = 1; /* w do dostępu do tablicy stanów */ void test (int k); if ((state [(k-1)% N] <> 2 ) and ( state[k]== 1) and ( state[(k+1)%n] <> 2 )) state [k] = 2; VB(filsem[k]); " Wykład 4 strona 22/24
process Philosopher (int name); while(1) /* myśli */ /* chce jeść */ PB(w); state[name] = 1; test (name); VB(w); PB ( filsem[name] ); /* filsem[name] moŝe zostać opuszczony, jeśli został podniesiony w procedurze test(name) */ request (fork[name], fork [(name+1) % N] ) ; /* je */ release (fork[name], fork [(name+1) % N]) ; PB(w); state[name] = 0; test ((name-1) % N); test ((name+1) % N); /* sprawdzamy obydwu sąsiadów czy czekają */ /* odpowiednie semafory są podnoszone */ VB(w); Wykład 4 strona 23/24
To samo z globalnymi semaforami semaphore sem [N] = (N) 1; void philosopher(int name); while(1) /* myśli */ PD(sem[name], sem[(name+1) % N]); /*je*/ VD(sem[name], sem[(name+1) % N]); } } Wykład 4 strona 24/24