Programowanie Równoległe i Rozproszone Lucjan Stapp Wydział Matematyki i Nauk Informacyjnych Politechnika Warszawska (l.stapp@mini.pw.edu.pl) 1/30 PRiR MONITOR klasa z następującymi właściwościami: wszystkie zmienne, typy, funkcje, deklaracje procedur znajdujące się w monitorze są prywatne (tzn. niepubliczne) z jednym wyjątkiem: funkcje (procedury) wejściowe (ang. entry) są publiczne. w dowolnym momencie co najwyżejjedna funkcja wejściowa danej instancji monitora może być wykonywana 2/30 PRiR Są dwa stany monitora: wolny (ang. free) żadna funkcja wejściowa (entry) nie jest wykonywana zajęty (ang. busy) dokładnie jedna funkcja wejściowa (entry) jest wykonywana 3/30 PRiR Przygotował: Lucjan Stapp 1
predefiniowany typ QUEUE typ danych (FIFO) z dwiema operacjami: void delay() jeśli proces wykonuje operację delay(), jego wykonywanie jest wstrzymywane i jest umieszczany na końcu kolejki q, monitor staje się wolny; delay() jest wykonywana wewnątrz funkcji wejściowej, więc monitor jest zajęty, gdy ta akcja się rozpoczyna. 4/30 PRiR predefiniowany typ QUEUE typ danych (FIFO) z dwiema operacjami (cd): void continue() proces P, który wykonuje q.continue() kończy swoje operacje wewnątrz monitora. Jeśli QUEUE q jest niepusta, pierwszy proces (P ) znajdujący się w QUEUE q wznawia swoje wykonywanie od pierwszej operacji po tym wywołaniu delay(), które umieściło P w q. Monitor jest cały czas zajęty. Jeśli QUEUE q jest pusta, monitor staje się wolny. 5/30 PRiR Schemat producent konsument z jednoelementowym buforem: 6/30 PRiR Przygotował: Lucjan Stapp 2
class buffer: public monitor int full = 0; /*full==0 wtw bufor jest pusty */ QUEUE qp, qc; /* qp kolejka producentów, qc - kolejka konsumentów */ product b; /* miejsce w buforze */ void entry::put(product S ); /*umieszczanie w buforze */ if (full) qp.delay(); /*bufor pełny, producent musi czekać*/ b=s; full=1; /*teraz bufor jest pełny */ qc.continue(); /* jeżeli czeka konsument..*/ void entry::get(product *S); /*pobieranie z bufora */ if (!full) qc.delay(); /*bufor jest pusty, konsument musi czekać */ S=b; full=0; /* teraz bufor jest pusty */ qp.continue(); /*jeżeli czeka producent. */ /* koniec monitora */ 7/30 PRiR process::producer (buffer B); product x; product production()...; void main() while(1) x = production(); B.put(x); process::consumer (buffer B); product x; void consumption()...; void main() while(1) B.get(&x); Consumption(); 8/30 PRiR Schemat producent konsument z jednoelementowym buforem, n producentów, m konsumentów Kod dla producenta, konsumenta i bufora nie ulega zmianie. 9/30 PRiR Przygotował: Lucjan Stapp 3
Schemat producent konsument, każdy proces działa jako producent i konsument: 10/30 PRiR Kod monitora buffer nie zmienia się.. process::prod_cons (buffer BL,BR); product x,y; product production()...; void consumption(product y)...; void main() while(1) x=production(); BR.put(x); BL.get(&y); consumption(y); 11/30 PRiR UWAGA (ZŁE ROZWIĄZANIE - deadlock): void main() while(1) x=production(); BL.get(&y); BR.put(x); consumption(y); 12/30 PRiR Przygotował: Lucjan Stapp 4
Schemat producent konsument z k - elementowym buforem, n producentami, m konsumentami: 13/30 PRiR Schemat producent konsument z k - elementowym buforem, n producentami, m konsumentami: Kod dla procesu jest taki sam jak dla jednoelementowego bufora, tylko kod monitora ulega zmianie. 14/30 PRiR class buf_k: public monitor (int k); /* k the size of buffer */ int first_free=0; /* wskaźnik do pierwszej wolnej komórki w buforze b*/; int first_occ=0; /* wskaźnik do pierwszej zajętej komórki w buforze b */; int no_of_elem = 0; /* liczba zajętych elementów w buforze*/ product b[]; /* bufor */ QUEUE qp, qc; /* qp kolejka producentów, qc kolejka konsumentów */ 15/30 PRiR Przygotował: Lucjan Stapp 5
void entry::put(product S ); /* umieszczenie w buforze */ if (no_of_elem==k) qp.delay(); /*bufor jest pełny, producent musi czekać */ no_of_elem++; b[first_free]=s; first_free+++; /* first_free=(first_free+1)%k; */ qc.continue(); /* jeżeli czeka konsument */ void entry::get(* product S); /*pobieranie z bufora */ if (no_of_elem==0) qc.delay(); /*bufor jest pusty, konsument musi czekać */ S=b[first_occ]; no_of_elem --; first_occ+++; /* first_occ=(first_occ+1)%k; */ qp.continue(); /*jeżeli czeka producent */ /* koniec kodu monitora buf_k */ 16/30 PRiR Czytelnicy i pisarze z priorytetem dla pisarzy Przypomnijmy sobie opis problemu: 1. Jest n czytelników i m pisarzy oraz jedna wspólna strona do komunikacji. 2. Każdy czytelnik czyta ze strony, każdy pisarz może pisać na stronie. 3. Wielu czytelników może naraz czytać stronę. 4. Pisarz zajmuje stronę na wyłączność (żaden inny proces pisarz czy czytelnik - nie może używać w tym czasie strony). 5. Pisarze są uprzywilejowani, co oznacza, że jeśli pisarz chce pisać, żaden czytelnik nie może zacząć czytać. 6. Nie ma priorytetów wśród pisarzy. 7. Nie ma ograniczeń czasowych na czytanie i pisanie, ale operacje te są skończone. 17/30 PRiR Czytelnicy i pisarze z priorytetem dla pisarzy: 18/30 PRiR Przygotował: Lucjan Stapp 6
Czytelnicy i pisarze z priorytetem dla pisarzy: Rozwiązanie: Pisarz: Robi coś; Chce pisać; Pisze; Informuje, że skończył pisać; Czytelnik: Robi coś; Chce czytać; Czyta; Informuje, że skończył czytać; 19/30 PRiR class reader_writer: public monitor int n_of_readers = 0; /* liczba aktualnie czytających procesów */ int n_of_ writers = 0; /* liczba aktualnie czekających pisarzy */ int writing = 0; /*writing > 0 pisarz pisze */ QUEUE q_of_r, q_of_w; /* q_of_r kolejka czytelników, q_of_w kolejka pisarzy */ 20/30 PRiR void entry::start_of_reader (); if (n_of_writers > 0 writing > 0) q_of_r.delay(); n_of_readers + + ; q_of_r.continue(); void entry::end_of_reader (); n_of_readers --; if (n_of_readers ==0) q_of_w.continue(); /* koniec kodu monitora */ void entry::start_of_writer (); if (writing >0 n_of_readers > 0) n_of_writers ++; q_of_w.delay (); n_of_writers--; writing = 1; void entry::end_of_writer (); writing = 0; if ( n_of_writers > 0) q_of_w.continue (); q_of_r. continue( ); 21/30 PRiR Przygotował: Lucjan Stapp 7
n filozofów jedzących rybę: 22/30 PRiR n filozofów jedzących rybę: Opis problemu: Jest n filozofów siedzących dookoł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. Do jedzenia ryby filozof potrzebuje obu widelców leżących obok niego. 23/30 PRiR Czynności filozofa: loop Myśli Chce podnieść widelce - Try_to_take_his_forks Je rybę posługując się dwoma widelcami Odkłada widelce - Put_down_forks 24/30 PRiR Przygotował: Lucjan Stapp 8
Rozwiązanie Procedura Try_to_take_his_forks Czekaj, aż oba widelce będą wolne Podnieś oba widelce Procedura Put_down_forks: Odłóż widelce - w dowolnej kolejności. Obudź sąsiada, który czeka na widelec (tylko, gdy jego drugi widelec jest wolny): Musimy tu być bardzo ostrożni, gdy obaj sąsiedzi czekają i oba odpowiednie widelce są wolne. 25/30 PRiR class TABLE: public monitor (int n); /* w poniższych operacjach +1,-1, +2, -2 są liczone modulo n */ int Fork [n]=0, WaitPh[n]=0, Wait=0; /*WaitPh[i] == 1 wtw i-ty filozof czeka*/ QUEUE qph[n]; void entry::trytotakeforks(int NofPh); if(fork[nofph]>0 Fork[NofPh-1]>0) WaitPh[NofPh]=1; qph[nofph].delay( ); WaitPh[NofPh]=0; ; Fork[NofPh]=1; Fork[NofPh-1]=1; if (Wait>0) Wait=0; qph[nofph+2].continue(); ; ; /* end of monitor TABLE */ void entry::putdownforks(int NofPh); Fork[NofPh]=0; Fork[NofPh-1]=0; if (WaitPh[NofPh+1]>0 && Fork[NofPh+1]==0) Wait = 1; if (WaitPh[NofPh-1]>0 && Fork[NofPh-2]==0) qph[nofph - 1 ].continue(); if (Wait) Wait = 0; qph[nofph + 1 ].continue(); ;; 26/30 PRiR process::phil (int NofPh, table TABLE); thinking(); table.trytotakeforks(nofph); eating(); table.putdownforks(nofph); 27/30 PRiR Przygotował: Lucjan Stapp 9
Jedzący filozofowie: szarzy filozofowie kontra biali filozofowie 28/30 PRiR Czynności filozofa nie zmieniają się. Proces podnoszenia widelców jest taki sam. Proces odkładania (putting_down_forks) filozofa z grupy szarych filozofów jest następujący: Zaczekaj, aż obaj sąsiedzi z grupy szarych filozofów zaczną jeść, Odłóż widelce. Efekt: Biali filozofowie nigdy nie jedzą zostaną zagłodzeni. 29/30 PRiR Definicja: Zagłodzenie(ang. starvation) występuje, gdy proces (albo grupa procesów) czeka w nieskończoność na wspólny zasób. Uwaga: Istnieje możliwość zagłodzenia w przypadku problemu czytelników i pisarzy z priorytetem dla pisarzy: czy to błąd? 30/30 PRiR Przygotował: Lucjan Stapp 10