Programowanie Równoległe i Rozproszone Lucjan Stapp Wydział Matematyki i Nauk Informacyjnych Politechnika Warszawska (l.stapp@mini.pw.edu.pl) 1/75 PRiR Trzy podstawowe zadania programów równoległych to: 1. Jednoczesna praca procesorów (równoległość wykonania) 2. Wymiana informacji (komunikacja między procesowa) 3. Wymuszenie na procesorach ustalenia jednej wartości dla zmiennej albo czasu rozpoczęcia i zakończenia (synchronizacja) 2/75 PRiR 1. Równoległość: Wybór między SIMD a MIMD 3/75 PRiR Przygotował: Lucjan Stapp 1
2. Komunikacja międzyprocesorowa (międzyprocesowa) Ani ludzie ani komputery nie mogą się komunikować bez ustalenia pewnego zbioru nazw. Architektura z pamięcią dzieloną vs multikomputery Problem komunikacji można rozwiązać na różne sposoby 1. architektura pamięci dzielonej szyny połączenie typu crossbar połączenie typu multistage 2. dla maszyn z pamięcią rozproszoną pierścienie drzewa binarne dwu-wymiarowe tablice procesorów hiperkostka (hypercube) Architektury te były prezentowane na pierwszym wykładzie 4/75 PRiR odnosi się do potrzeby ustalenia jednej wartości dla zmiennej przez dwa (lub więcej) procesory. Dwa podstawowe problemy to: 1. wzajemne wykluczanie 2. spójność pamięci Wzajemne wykluczanie(ang. mutual exclusion) zezwalanie dokładnie jednemu procesorowi na dostęp do danego miejsca w pamięci w tym samym czasie. 5/75 PRiR Przykład 1 Proc_1: I_11: x 1 Proc_2: I_21: x 2 Wartość początkowa : x=0 6/75 PRiR Przygotował: Lucjan Stapp 2
Przykład 1 cd Możliwe scenariusze wykonania: Time Proc_1 Proc_2 x 0 x 1 1 x 2 2 Time Proc_1 Proc_2 x 0 x 2 2 x 1 1 Stądmożliwewartościxto1lub2. 7/75 PRiR Przykład 2. Proc_1: y x+1; x y; z z + y; Proc_2: y x+1; x y; z z + y; Początkowe wartości : x=0, y=0, z=0 8/75 PRiR Przykład 2 cd. Możliwe scenariusze wykonania: Time Proc_1 Proc_2 x y z 0 0 0 y x+1 0 1 0 x y 1 1 0 z z + y 1 1 1 y x+1 1 2 1 x y 2 2 1 z z+y 2 2 3 Time Proc_1 Proc_2 x y z 0 0 0 y x+1 0 1 0 y x+1 0 1 0 x y 1 1 0 x y 1 1 0 z z+y 1 1 1 z z+y 1 1 2 Time Proc_1 Proc_2 x y z 0 0 0 y x+1 0 1 0 x y 1 1 0 y x+1 1 2 0 z z + y x y 2 2 2 z z+y 2 1 4 9/75 PRiR Przygotował: Lucjan Stapp 3
Przykład 2 cd. Możliwe wartości końcowe: x y z 2 2 3 1 1 2 2 1 4 10/75 PRiR Wniosek: Wzajemne wykluczanie NIEwystarcza, aby zapewnić poprawne wykonanie programu równolegle wykonywanego. 11/75 PRiR Definicje Proces sekwencyjna część programu równolegle wykonywanego (równoległego), wykonywana na jednym procesorze. Liczba procesów może być większa niż liczba procesorów w systemie. (Rozwiązanie powyższego problemu zostanie podane w przyszłości). Sekcja krytyczna sposób na poprawną komunikację. Co najwyżej jeden proces może pracować w sekcji krytycznej, wykorzystując w tym samym czasie wspólne zasoby (np. zmienne) 12/75 PRiR Przygotował: Lucjan Stapp 4
Przykład 2. cd Proc_1: critical section y x+1; x y; z z + y; end of critical section Proc_2: critical section y x+1; x y; z z + y; end of critical section Początkowe wartości: x=0, y=0, z=0 13/75 PRiR Przykład 2. cd Możliwe scenariusze wykonania: Time Proc_1 Proc_2 x y z 0 0 0 y x+1 0 1 0 x y 1 1 0 z z + y 1 1 1 y x+1 1 2 1 x y 2 2 1 z z+y 2 2 3 Time Proc_1 Proc_2 x y z 0 0 0 y x+1 0 1 0 x y 1 1 0 z z+y 1 1 1 y x+1 1 2 1 x y 2 2 1 z z + y 2 2 3 14/75 PRiR Example 2 - cont. Jedyny możliwy wynik: x y z 2 2 3 15/75 PRiR Przygotował: Lucjan Stapp 5
Wniosek Komunikacja między procesami jest oparta na wzajemnym wykluczaniu się sekcji krytycznych 16/75 PRiR Pierwszy (podstawowy) schemat komunikacji Przykład 4 - komunikacja Proc_1: cycle instrukcja_1_a; wysyłanie informacji; instrukcja_1_b; end_of_cycle Proc_2: cycle instrukcja_2_a; odbieranie informacji; instrukcja_2_b; end_of_cycle 17/75 PRiR Pierwszy schemat komunikacji Example 4a(Producent - konsument) Proc_1: cycle instruction_1_a; critical_section wysyłanie informacji do pustego bufora; (zapis do pustego bufora); end_of_cycle; Proc_2: cycle instruction_2_a; critical_section odbieranie informacji z niepustego bufora (czytanie z bufora); end_of_cycle; 18/75 PRiR Przygotował: Lucjan Stapp 6
Drugi (podstawowy) schemat komunikacji Drugi podstawowy schemat wykorzystywany do komunikacji jest używany głównie w systemach rozproszonych handshaking (ang. uścisk dłoni); 19/75 PRiR Cycle Instruction_1_A; critical_section; wysłanie sygnału; Drugi schemat komunikacji critical_section; czekanie na odpowiedź; critical_section; pisanie (wysyłanie) danych; critical_section; czekanie na end_signal; end_of_cycle; Cycle Instruction_2_A; critical_section; czekanie na sygnał; wysłanie odpowiedzi; wait; critical_section; odbieranie danych; wysłanie end_signal; end_of_cycle; 20/75 PRiR Proc_1: cycle instruction_1_a; critical_section czekaj_dopóki_bufor_2_jest_pusty; critical_section czytaj_z_bufor_2; Przykład 5 Proc_2: cycle instruction_2_a; critical_section czekaj_dopóki_bufor_1_jest_pusty; critical_section czytaj_z_bufor_1; end_of_critical_section instruction_1_b; instruction_2_b; critical_section czekaj_dopóki_bufor_1_jest_pełny; pisz_do_bufor_1; end_of_cycle; critical_section czekaj_dopóki_bufor_2_jest_pełny; pisz_do_bufor_2; end_of_cycle; 21/75 PRiR Przygotował: Lucjan Stapp 7
Deadlock W powyższej sytuacji oba procesy będą czekały w nieskończoność na dane: Proc_1 na dane z bufora_2 Proc_2 na dane z bufora_1 Powyższa sytuacja to tzw. zakleszczenie(ang. deadlock). Zakleszczenie to ZAWSZEkrytyczny błąd programisty. 22/75 PRiR Teraz zademonstrujemy narzędzia do zarządzania sekcją krytyczną. Wymagania stawiane rozwiązaniom: 1. Symetryczność i równość procesów. 2. Niezależne od prędkości procesów. 3. Skończony czas rozwiązywania konfliktów. 4. Niezależne czynności poza sekcją krytyczną. 23/75 PRiR SYNCHRONIZACJA PRZEZ ZMIENNĄ CAŁKOWITĄ (LICZNIK) intx= 0; /* x = 0 gdy nie ma żadnego procesu w sekcji krytycznej x = 1 gdy jest jakiś proces w sekcji krytycznej */ process:: P; while(1) instructions_a; while (!x) x++; if ( x<> 1 ) x--; loop; } } /* teraz sekcja krytyczna */ /* po sekcji krytycznej */ x --; } /*while*/ } /* P */ 24/75 PRiR Przygotował: Lucjan Stapp 8
SYNCHRONIZACJA PRZEZ ZMIENNĄ CAŁKOWITĄ (LICZNIK) Powyższe rozwiązanie jest błędne: Z dwoma procesami pracującymi krok po kroku, x może przyjąć wartość 2. Żaden proces nie wejdzie do sekcji krytycznej. 25/75 PRiR SYNCHRONIZACJA PRZEZ ZMIENNE LOKALNE int x:= 0; /* x = 0 gdy nie ma żadnego procesu w sekcji krytycznej x = 1 gdy jest jakiś proces w sekcji krytycznej */ process :: P; int y = 1; while(1) instructions_a; x y; /* operacja atomowa zamiana wartości */ while (y) x y; /* teraz sekcja krytyczna */ /* po sekcji krytycznej */ x y; }/*while*/ }/* P */ Wniosek: aktywne czekanie(busy waiting) 26/75 PRiR SYNCHRONIZACJA PRZEZ (Semafory Dijkstry) Semafor:specjalna zmienna skojarzona z sekcją krytyczną. Są dwie atomowe (niepodzielne)operacje na semaforze s: P(s): opuść semafor s (=czekaj na semaforze s) Wait(s) V(s): podnieś semafor s (= sygnalizuj na semaforze s) Signal(s) 27/75 PRiR Przygotował: Lucjan Stapp 9
Zarys kodu semafora:: class semaphore:: public s; ints; SET qs; /* zbiór czekających procesów*/ void Wait(s) if (s=< 0) dodaj_proces_do_qs; else s--; void Signal(s) if (not_empty(qs) ) aktywuj_jakiś_proces_z_qs; else s++; } } 28/75 PRiR Są 3 stany semafora: 1. s > 0 2. s = 0 i qs jest pusty 3. s = 0 i qs nie jest pusty. Wait(s): s = 0 Tak Nie uśpij proces w qs s-- 29/75 PRiR Signal(s): s = 0 Tak Nie qs jest pusty s++ Tak s++ Nie aktywuj jakiś proces z qs Ponieważ stan s> 0 i qs niepusta jest niemożliwy upraszczamy Signal(s) (zaznaczona część powyższej ilustracji). 30/75 PRiR Przygotował: Lucjan Stapp 10
Semafory spełniają nasze wymagania. 1. Symetryczność i równość procesów. 2. Niezależne od prędkości procesów. 3. Skończony czas rozwiązywania konfliktów. 4. Niezależne od czynności poza sekcją krytyczną. 31/75 PRiR Dostęp do sekcji krytycznej - typowy schemat semaphore w = 1; process:: P; while(1) instruction_a; Wait(w); /* opuść semafor w*/ /* wejście do sekcji krytycznej*/ /* sekcja krytyczna*/ /*wyjście z sekcji krytycznej */ Signal(w); /*podnieś semafor w*/ } /*while*/ } /* P */; 32/75 PRiR Producent -konsument z jednoelementowym buforem 33/75 PRiR Przygotował: Lucjan Stapp 11
buffer buf; semaphore full, empty = 0, 1; process producer; Product S; Product production()...;} while(1) S= production (); Wait(empty); /*czekanie na pusty bufor*/ buf=s; Signal(full); /* sygnalizuj, że coś jest w buforze*/ } process consumer; Void consumption( product S)...;} product S; while(1) Wait(full); /* czekaj aż coś będzie w buforze*/ S=buf; Signal(empty); /*sygnalizuj, że bufor jest pusty*/ consumption(s); } } 34/75 PRiR Dlaczego potrzeba 2 semaforów(full oraz empty)? Czy zawszefull +empty == 1? 35/75 PRiR Schemat producent - konsument: jednoelementowy bufor, m producentów, n konsumentów Kod dla producenta i konsumenta nie ulegazmianie. 36/75 PRiR Przygotował: Lucjan Stapp 12
Schemat producent - konsument każdy proces jest jednocześnie producentem i konsumentem 37/75 PRiR buffer buf1, buf2; semaphore full1,empty1,full2,empty2 = 0,1,0,1; /*dla każdego bufora potrzebujemy dwóch semaforów*/ process::p1; product S,C; while(1) /* produkcja dobuf2 */ S= production(); Wait(empty2); buf2 = S; Signal(full2); /*pobranie zbuf1 */ Wait(full1); C=buf1; Signal(empty1); Consumption(C); process::p2; Product S,C; while(1) /* produkcjado buf1 */ S= production(); Wait(empty1); buf1 =S; Signal(full1); /* pobranie zbuf2 */ Wait(full2); C=buf2 ; Signal(empty2); Consumption(C); 38/75 PRiR Schemat producent konsument z nieskończonym buforem 39/75 PRiR Przygotował: Lucjan Stapp 13
buffer buf[ ]; int first_free = 1; /* pierwsza pusta komórka w buforze */ intlast_occ = 1; /* ostatnia komórka w buforze z nieprzeczytanymi informacjami */ semaphore full, wp, wc = 0,1,1; /* fulldo sprawdzenia czy czytanie jest możliwe; fulljest równe liczbie pełnych (nieprzeczytanych) komórek w buforze; bufor jest nieskończony, dlatego dodanie do bufora jest zawsze możliwe */ /* wp dla wzajemnego wykluczania się producentów */ /* wcdla wzajemnego wykluczania się konsumentów */ 40/75 PRiR process :: producer; product S; while(1) S= production(); Wait(wp); /* opuść semafor wp, aby zapewnić dostęp do bufora na wyłączność */ buf[first_free] = S; /* wstaw produkt na pierwsze wolne miejsce w buforze*/ first_free ++ ; Signal(full); /* podnieś semafor wp, aby udostępnić dostęp innym producentom do bufora */ Signal(wp); process :: consumer; product S; while(1) Wait(full); /* konsumpcja jest możliwa tylko gdy coś jest w buforze*/ Wait(wc); /* opuść semafor wc, aby uzyskać dostęp do bufora na wyłączność */ S=buf[last_occ]; last_occ++; Signal(wc); /* podnieśsemaforwcby udostępnić innym konsumentom dostęp do bufora*/ consumption(s); 41/75 PRiR BINARNE Binarny semafor (ang. binary semaphore) ma tylko 2 wartości: 0 i 1. Zamiast operacji Wait i Signal używamy analogicznych operacji binarnych: BWait i BSignal. 42/75 PRiR Przygotował: Lucjan Stapp 14
BINARNE Producent konsument z nieskończonym buforem i semaforami binarnymi Zamiast semafora full powinno się użyć: zmienną całkowitą m = liczba pełnych komórek w buforze binarny semafor bfull binarny semafor wm pilnujący dostępu do zmiennej m 43/75 PRiR BINARNE buffer buf[ ] ; intfirst_free= 1; /* pierwsza wolna komórka w buforze*/ intlast_occ = 1; /* ostatnia komórka z nieprzeczytanymi informacjami*/ binary semaphore bfull, wp, wc, wm = 0,1,1,1; /* wm -semafor dostępu do zmiennej m*/ intm = 0; /* liczba wypełnionych komórek*/ 44/75 PRiR process :: producer; product S; while(1) S= production(); BWait(wp); buf[first_free] = S; first_free ++ ; BSignal(wp); /*poniższa sekwencja instrukcji jest użyta zamiast Signal(full)z poprzedniego przykładu*/ BWait(wm); m++; if (m ==1) BSignal(bfull); Bsignal(wm); BINARNE process :: consumer; product S; while(1) Bwait(bfull); /* konsumpcja jest możliwa gdy coś jest w buforze*/ Bwait(wc); S=buf[last_occ]; last_occ++; BSignal(wc); BWait(wm); m--; if (m >0) Bsignal (bfull); BSignal(wm); /* jeżeli bufor jest niepusty, musimy otworzyć semafor binarny bfull*/ consumption(s); 45/75 PRiR Przygotował: Lucjan Stapp 15
BINARNE Uwaga: Celem powyższego przykładu jest pokazanie, jak zastąpić semafor zwykły semaforem binarnym; czasami może to prowadzić do dodatkowych komplikacji; nie będą one omawiane na niniejszym wykładzie. 46/75 PRiR Producent konsument ze skończonym buforem 47/75 PRiR Producent - konsument ze skończonym buforem Nowy semafor emptypowinien zostać dodany do poprzedniego rozwiązania. Umieszczenie elementu w buforze jest możliwe, gdy empty(liczba wolnych komórek w buforze) jest większa niż 0. W przeciwnym wypadku producent powinien czekać (w zbiorze na semaforze empty). 48/75 PRiR Przygotował: Lucjan Stapp 16
#define N =??? /*wielkość bufora*/ buffer buf[ ] ; int first_free = 1; /* pierwsza wolna komórka w buforze*/ intlast_occ = 1; /* ostatnia komórka w buforze z nieprzeczytaną informacją*/ semaphore full, empty = 0,N; binary semaphore wp, wc= 1,1; 49/75 PRiR process :: producer; product S; while(1) S= production(); Wait(empty); BWait(wp); buf[first_free] = S; first_free=(first_free + 1)%N; BSignal(wp); Signal(full); process :: consumer; product S; while(1) Wait(full); BWait(wc); S=buf[last_occ]; last_occ=(last_occ+1)%n; BSignal(wc); Signal(empty); consumption(s); 50/75 PRiR Problem czytelników i pisarzy Readers writers problem 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. 51/75 PRiR Przygotował: Lucjan Stapp 17
Schemat: czytelnicy i pisarze 52/75 PRiR Readers writers problem Rozwiązanie: Pisarz: Robi coś; Chce pisać; Pisze; Informuje, że skończył pisać; Czytelnik: Robi coś; Chce czytać; Czyta; Informuje, że skończył czytać; 53/75 PRiR intno_of_r; /* liczba procesów - czytelników aktualnie czytających */ binarysemaphore 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 */ 54/75 PRiR Przygotował: Lucjan Stapp 18
process :: reader; while(1) BWait(w); no_of_r ++; if (no_of_ r == 1) BWait(sp); /* pierwszy czytelnik opuszcza sp */ BSignal(w); /* czytanie ze strony*/ BWait(w); no_of_r--; if (no_of_r== 0) BSignal(sp); /* ostatni czytelnik podnosi semafor sp*/ BSignal(w); process :: writer; while(1) BWait(sp); /* każdy piszący proces musi opuścić semafor sp*/ /* pisanie na stronie */ BSignal(sp); 55/75 PRiR Czytelnicy - pisarze z priorytetem dla pisarzy. Dodajemy dwa dodatkowe warunki do zadania: 1. Pisarze mają priorytet, jeśli jakikolwiek pisarz chcepisać, żaden czytelnik nie może zacząć czytać. 2. Nie ma priorytetu pomiędzy czytelnikami. 56/75 PRiR int no_of_r,no_of_w = 0,0; /*no_of_r- liczba procesów aktualnie czytających no_of_w liczba procesów, które chca pisać*/ binary semaphore sp,sr= 1, 1; binary semaphore w1, w2, w3= 1, 1,1; /*w1 do dostępu do no_of_r w2 do dostępu do no_of_w w3 dodatkowa śluza dla czytelników sp podobnie jak w poprzednim przykładzie sr dla priorytetu pisarzy */ 57/75 PRiR Przygotował: Lucjan Stapp 19
process :: reader; while(1) BWait(w3); BWait(sr); BWait(w1); no_of_r++; if (no_of_r == 1) BWait(sp); BSignal(w1); BSignal(sr); BSignal(w3); /* czytanie */ BWait(w1); no_of_r--; if ( no_of_r == 0) BSignal(sp); BSignal(w1); process :: writer; ; while(1) BWait(w2); no_of_w++; if (no_of_w== 1 ) BWait(sr); BSignal(w2); BWait(sp); /* pisanie */ BSignal(sp); BWait(w2); no_of_w--; if (no_of_w==0) BSignal(sr); BSignal(w2); 58/75 PRiR Modyfikacje semaforów Rozszerzone operacje na semaforach Jednoczesne operacje na semaforach (ang. Concurrent semaphore operations) (Dijkstra) DWait(s 1,s 2,...,s i,...,s n ); Czekaj aż wszystkie s i, s i >0 (i=1,...,n); for (i=1;i<=n;i++) s i = s i -1; DSignal(s 1,s 2,...,s i,...,s n ); for (i=1;i<=n;i++) s i = s i +1; 59/75 PRiR UOGÓLNIONE Modyfikacje semaforów Rozszerzone operacje na semaforach Uogólnione operacje na semaforach Wartość semafora jest zmieniana o wartość całkowitą n. NWait(s,n); Czekaj ażs >= n; s=s-n; NSignal(s,n); s = s+n; 60/75 PRiR Przygotował: Lucjan Stapp 20
UOGÓLNIONE Modyfikacje semaforów Rozszerzone operacje na semaforach Uogólnione operacje na semaforach Priorytety w dostępie do zasobów Priorytety są realizowane na podstawie numeru procesu. Procesy z mniejszymi numerami mają wyższy priorytet. 61/75 PRiR UOGÓLNIONE semaphore prior = 2*N-1; // N number of processes process:: Proc(name) /*1 <=name<=n */ int i; while(1) NWait(prior,N+name-1); /* prior == N-name żaden inny proces nie może wejść do sekcji krytycznej*/ /*zażądaj zasobu; */ /*critical section */ /*zwolnienie zasobu*/ NSignal(prior, name-1); /* prior = =N-1 żaden proces nie może wejść do sekcji krytycznej */ for(i=1;i<=n;i++) /* szukanie czekającego procesu z najwyższym priorytetem*/ NSignal(prior,1); /* prior == N+i-1 proces nr i wchodzi do sekcji krytycznej przerywamy pętlę */ } 62/75 PRiR UOGÓLNIONE Czytelnicy i pisarze bez priorytetu semaphore w = M; /* M > liczba czytelników*/ process :: reader; while(1) NWait(w,1); /*M procesów może zmniejszyć wo1*/ /* teraz czytanie */ NSignal(w,1); process :: writer; while(1) NWait(w,M); /*tylko jedenprocesmoże zmniejszyć w do 0*/ /* teraz pisanie */ NSignal(w,M); 63/75 PRiR Przygotował: Lucjan Stapp 21
UOGÓLNIONE Czytelnicy - pisarze z priorytetem dla pisarzy int no_of_r, no_of_w = 0,0; /*no_of_r liczba aktualnie czytających procesów no_of_w liczba czekających 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 */ 64/75 PRiR UOGÓLNIONE process :: reader; while(1) NWait(r,M); NWait(sp,1); NSignal(r,M-1); /*pisarz może zwiększyć semafor ro 1 */ NSignal(r,1); /*tu czytelnik czeka jeśli jakiś pisarz czeka */ /* czytanie */ NSignal(sp,1); process :: writer; while(1) NWait(r,1); NWait(sp,M); /* pisanie */ NSignal(sp,M); NSignal(r,1); 65/75 PRiR n filozofów je rybę 66/75 PRiR Przygotował: Lucjan Stapp 22
n filozofów je rybę 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: Loop Myśli Chce podnieść swoje widelce Je rybę posługując się widelcami Odkłada widelce 67/75 PRiR n filozofów je rybę. 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: deadlock 68/75 PRiR n filozofów je rybę. Rozwiązanie problemu: Spróbuj podnieść swoje widelce Rozwiązanie 2: Loop Czekaj aż lewy jest wolny Podnieś lewy IF prawy jest zajęty Odłóż lewy widelec ELSE Podnieś prawy widelec EXIT } } Złe rozwiązanie: busy waiting 69/75 PRiR Przygotował: Lucjan Stapp 23
n filozofów je rybę. Rozwiązanie problemu: Spróbuj podnieść swoje widelce Wniosek: Rozwiązanie 3: Czekaj, aż oba będą wolne Podnieś oba widelce w jednym ruchu 70/75 PRiR n filozofów je rybę. Rozwiązanie problemu: Odłóż widelce Odłóż widelce w dowolnej kolejności Obudźtego sąsiada, który czeka na widelec (jeśli jego drugi widelec jest wolny) czasami obu 71/75 PRiR Pierwsze rozwiązanie z semaforami - deadlock resource fork [N-1]; binary semaphore sem [N-1] = (N) 1; process Philosopher (name); while(1) /* myśli */ /* chce jeść */ BWait( sem[name] ); /* podnosi lewy widelec - fork[name] */ BWait( sem[(name+1) % N] ) ; /*podnosi prawy widelec -fork[(name+1) % N] */ /* jedzenie*/ BSignal( sem[name] ); /*odłożenie widelca fork[name] */ BSignal( sem[(name+1) % N] ) ; /* odłożenie widelca fork [(name+1) % N] */ 72/75 PRiR Przygotował: Lucjan Stapp 24
Prawidłowe 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-1]; binary semaphore filsem[n-1] = 0; /*każdy filozof ma swój własny semafor do czekania */ intstate [N-1] = 0 ; Boolean semaphore w = 1; /* w do dostępu do tablicy stanów */ void test (k: integer); if ((state [(k-1)% N] <> 2 ) and ( state[k]== 1) and ( state[(k+1)%n] <> 2 )) state [k] = 2; BSignal(filsem[k]); 73/75 PRiR Prawidłowe rozwiązanie(cd.) process Philosopher (name); while(1) /* mysli */ /* chce jeść*/ BWait(w); state[name] = 1; test (name); BSignal(w); BWait( filsem[name] ); /* filsem[name] może zostać opuszczony, TYLKOjeśli został podniesiony w procedurze test(name) */ request (fork[name], fork [(name+1) % N] ) ; /* eating*/ release (fork[name], fork [(name+1) % N]) ; BWait(w); state[name] = 0; test ((name-1) % N); test ((name+1) % N); /* sprawdzamy obydwu sąsiadów czy czekają */ /* odpowiednie semafory są podnoszone */ BSignal(w); 74/75 PRiR To samo z semaforami uogólnionymi: semaphore sem[n] = 1; void philosopher(int name); while(1) /* myśli*/ DWait(sem[name], sem[(name+1) % N]); /*je*/ DSignal(sem[name], sem[(name+1) % N]); } } 75/75 PRiR Przygotował: Lucjan Stapp 25