wykład 3 - MiNI PW - Politechnika Warszawska
Transkrypt
wykład 3 - MiNI PW - Politechnika Warszawska
Programowanie Równoległe i Rozproszone Wykład 3 Programowanie Równoległe i Rozproszone Lucjan Stapp Wydział Matematyki i Nauk Informacyjnych Politechnika Warszawska ([email protected]) 1/75 PRiR Wykład 3 Synchronizacja 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 Wykład 3 Synchronizacja 1. Równoległość: Wybór między SIMD a MIMD 3/75 Przygotował: Lucjan Stapp PRiR Wykład 3 1 Programowanie Równoległe i Rozproszone Wykład 3 Synchronizacja 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 Wykład 3 Synchronizacja Synchronizacja Synchronizacja 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 Wykład 3 Synchronizacja Przykład 1 Proc_1: I_11: x←1 Proc_2: I_21: x←2 Wartość początkowa : x=0 6/75 Przygotował: Lucjan Stapp PRiR Wykład 3 2 Programowanie Równoległe i Rozproszone Wykład 3 Synchronizacja Przykład 1 cd Możliwe scenariusze wykonania: Time Proc_1 Proc_2 x 0 1 x←2 2 x←1 Time Proc_1 Proc_2 x 0 x←2 2 x←1 1 Stąd możliwe wartości x to 1 lub 2. 7/75 PRiR Wykład 3 Synchronizacja 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 Wykład 3 Synchronizacja Time Przykład 2 cd. Możliwe scenariusze wykonania: 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 1 2 1 y←x+1 Time Proc_1 Proc_2 y←x+1 y←x+1 x←y x←y z←z + y z←z + y x y z 0 0 0 x←y 2 2 1 z←z + y 2 2 3 Time Proc_1 Proc_2 x y z 0 0 0 0 0 1 0 y←x+1 0 1 0 1 0 x←y 1 1 0 1 1 0 1 2 0 1 1 0 1 1 1 1 1 2 y←x+1 z←z + y 9/75 Przygotował: Lucjan Stapp x←y 2 2 2 z←z + y 2 1 4 PRiR Wykład 3 3 Programowanie Równoległe i Rozproszone Wykład 3 Synchronizacja 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 Wykład 3 Synchronizacja Wniosek: Wzajemne wykluczanie NIE wystarcza, aby zapewnić poprawne wykonanie programu równolegle wykonywanego. 11/75 PRiR Wykład 3 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 Przygotował: Lucjan Stapp PRiR Wykład 3 4 Programowanie Równoległe i Rozproszone Wykład 3 Synchronizacja 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 Wykład 3 Synchronizacja 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 1 2 1 y←x+1 Time Proc_1 x←y 2 2 1 z←z + y 2 2 3 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 Proc_2 PRiR Wykład 3 Synchronizacja Example 2 - cont. Jedyny możliwy wynik: x y z 2 2 3 15/75 Przygotował: Lucjan Stapp PRiR Wykład 3 5 Programowanie Równoległe i Rozproszone Wykład 3 Wniosek Komunikacja między procesami jest oparta na wzajemnym wykluczaniu się sekcji krytycznych 16/75 PRiR Wykład 3 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 Wykład 3 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_critical_section; end_of_cycle; Proc_2: cycle instruction_2_A; critical_section odbieranie informacji z niepustego bufora (czytanie z bufora); end_of_critical_section; end_of_cycle; 18/75 Przygotował: Lucjan Stapp PRiR Wykład 3 6 Programowanie Równoległe i Rozproszone Wykład 3 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 Wykład 3 Drugi schemat komunikacji Cycle Cycle Instruction_1_A; critical_section; wysłanie sygnału; end_of_critical_section; Instruction_2_A; critical_section; czekanie na sygnał; wysłanie odpowiedzi; end_of_critical_section; critical_section; czekanie na odpowiedź; end_of_critical_section; critical_section; pisanie (wysyłanie) danych; end_of_critical_section; critical_section; czekanie na end_signal; end_of_critical_section; end_of_cycle; wait; critical_section; odbieranie danych; wysłanie end_signal; end_of_critical_section; end_of_cycle; 20/75 PRiR Wykład 3 Przykład 5 Proc_1: cycle instruction_1_A; critical_section czekaj_dopóki_bufor_2_jest_pusty; end_of_critical_section; critical_section czytaj_z_bufor_2; end_of_critical_section; instruction_1_b; critical_section czekaj_dopóki_bufor_1_jest_pełny; pisz_do_bufor_1; end_of_critical_section; end_of_cycle; Proc_2: cycle instruction_2_A; critical_section czekaj_dopóki_bufor_1_jest_pusty; end_of_critical_section; critical_section czytaj_z_bufor_1; end_of_critical_section instruction_2_B; critical_section czekaj_dopóki_bufor_2_jest_pełny; pisz_do_bufor_2; end_of_critical_section; end_of_cycle; 21/75 Przygotował: Lucjan Stapp PRiR Wykład 3 7 Programowanie Równoległe i Rozproszone Wykład 3 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 ZAWSZE krytyczny błąd programisty. 22/75 PRiR Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY SYNCHRONIZACJA PRZEZ ZMIENNĄ CAŁKOWITĄ (LICZNIK) int x= 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 Przygotował: Lucjan Stapp PRiR Wykład 3 8 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY SYNCHRONIZACJA PRZEZ SEMAFORY (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 Przygotował: Lucjan Stapp PRiR Wykład 3 9 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY Zarys kodu semafora:: class semaphore:: public s; { int s; 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY Signal(s): s=0 Tak Nie s++ qs jest pusty 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 Przygotował: Lucjan Stapp PRiR Wykład 3 10 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY Producent - konsument z jednoelementowym buforem 33/75 Przygotował: Lucjan Stapp PRiR Wykład 3 11 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY buffer buf; semaphore full, empty = 0, 1; process producer; Product S; Product production() { .....;} void main() {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) { .....;} void main() { 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 Wykład 3 SEMAFORY Dlaczego potrzeba 2 semaforów (full oraz empty)? Czy zawsze full + empty == 1 ? 35/75 PRiR Wykład 3 SEMAFORY Schemat producent - konsument: jednoelementowy bufor, m producentów, n konsumentów Kod dla producenta i konsumenta nie ulega zmianie. 36/75 Przygotował: Lucjan Stapp PRiR Wykład 3 12 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY Schemat producent - konsument każdy proces jest jednocześnie producentem i konsumentem 37/75 PRiR Wykład 3 SEMAFORY 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; void main() {while(1) { /* produkcja do buf2 */ S= production(); Wait(empty2); buf2 = S; Signal(full2); /*pobranie z buf1 */ Wait(full1); C=buf1; Signal(empty1); Consumption (C); }; }; process::P2; Product S,C; void main() {while(1) { /* produkcja do buf1 */ S= production(); Wait(empty1); buf1 =S; Signal(full1); /* pobranie z buf2 */ Wait(full2); C=buf2 ; Signal(empty2); Consumption (C); }; }; 38/75 PRiR Wykład 3 SEMAFORY Schemat producent konsument z nieskończonym buforem 39/75 Przygotował: Lucjan Stapp PRiR Wykład 3 13 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY buffer buf [ ]; int first_free = 1; /* pierwsza pusta komórka w buforze */ int last_occ = 1; /* ostatnia komórka w buforze z nieprzeczytanymi informacjami */ semaphore full, wp, wc = 0,1,1; /* full do sprawdzenia czy czytanie jest możliwe; full jest 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 */ /* wc dla wzajemnego wykluczania się konsumentów */ 40/75 PRiR Wykład 3 SEMAFORY process :: producer; { product S; void main() { 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; void main() { 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ś semafor wc by udostępnić innym konsumentom dostęp do bufora */ consumption(S); }; }; }; 41/75 PRiR Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 14 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY BINARNE buffer buf [ ] ; int first_free = 1; /* pierwsza wolna komórka w buforze*/ int last_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*/ int m = 0; /* liczba wypełnionych komórek */ 44/75 PRiR Wykład 3 SEMAFORY BINARNE process :: producer; { product S; void main() { 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); }; }; }; process :: consumer; { product S; void main() { 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 Wykład 3 Przygotował: Lucjan Stapp 15 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY Producent konsument ze skończonym buforem 47/75 PRiR Wykład 3 SEMAFORY Producent - konsument ze skończonym buforem Nowy semafor empty powinien 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 Przygotował: Lucjan Stapp PRiR Wykład 3 16 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY #define N = ??? /*wielkość bufora */ buffer buf [ ] ; int first_free = 1; /* pierwsza wolna komórka w buforze */ int last_occ = 1; /* ostatnia komórka w buforze z nieprzeczytaną informacją */ semaphore full, empty = 0,N; binary semaphore wp, wc = 1,1; 49/75 PRiR Wykład 3 SEMAFORY process :: producer; { product S; void main() { 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; void main() { 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 Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 17 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY Schemat: czytelnicy i pisarze 52/75 PRiR Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY int no_of_r; /* liczba procesów - czytelników aktualnie czytających */ binary 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 */ 54/75 Przygotował: Lucjan Stapp PRiR Wykład 3 18 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY process :: reader; void main() { 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; void main() { while(1) { BWait(sp); /* każdy piszący proces musi opuścić semafor sp */ /* pisanie na stronie */ BSignal(sp); }; }; 55/75 PRiR Wykład 3 SEMAFORY Czytelnicy - pisarze z priorytetem dla pisarzy. Dodajemy dwa dodatkowe warunki do zadania: 1. Pisarze mają priorytet, jeśli jakikolwiek pisarz chce pisać, żaden czytelnik nie może zacząć czytać. 2. Nie ma priorytetu pomiędzy czytelnikami. 56/75 PRiR Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 19 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY process :: reader; void main() { 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; void main(); { 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 Wykład 3 SEMAFORY Modyfikacje semaforów Rozszerzone operacje na semaforach Jednoczesne operacje na semaforach (ang. Concurrent semaphore operations) (Dijkstra) DWait(s1,s2,...,si,...,sn); Czekaj aż wszystkie si, si>0 for (i=1;i<=n;i++) si = si-1; (i=1,...,n); DSignal(s1,s2,...,si,...,sn); for (i=1;i<=n;i++) si = si+1; 59/75 PRiR Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 20 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY UOGÓLNIONE Czytelnicy i pisarze bez priorytetu semaphore w = M; /* M > liczba czytelników */ process :: reader; process :: writer; { { while(1) while(1) { { NWait(w,1); NWait(w,M); /*M procesów może zmniejszyć w o 1*/ /*tylko jeden proces może /* teraz czytanie */ zmniejszyć w do 0*/ NSignal(w,1); /* teraz pisanie */ }; NSignal(w,M); }; }; 63/75 Przygotował: Lucjan Stapp PRiR Wykład 3 21 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY UOGÓLNIONE process :: reader; void main() { while(1) { NWait(r,M); NWait(sp,1); NSignal(r,M-1); /*pisarz może zwiększyć semafor r o 1 */ NSignal(r,1); /*tu czytelnik czeka jeśli jakiś pisarz czeka */ /* czytanie */ NSignal(sp,1); }; }; process :: writer; void main() { while(1) { NWait(r,1); NWait(sp,M); /* pisanie */ NSignal(sp,M); NSignal(r,1); }; }; 65/75 PRiR Wykład 3 SEMAFORY n filozofów je rybę 66/75 Przygotował: Lucjan Stapp PRiR Wykład 3 22 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 23 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 24 Programowanie Równoległe i Rozproszone Wykład 3 SEMAFORY 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 */ int state [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 Wykład 3 SEMAFORY 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, TYLKO jeś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 Wykład 3 SEMAFORY 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 Przygotował: Lucjan Stapp PRiR Wykład 3 25