150875 Grzegorz Graczyk numer indeksu imię i nazwisko 151021 Paweł Tarasiuk numer indeksu imię i nazwisko Data 2011-11-07 Kierunek Informatyka Specjalizacja Inżynieria Oprgoramowania i Analiza Danych Rok akademicki 2011/12 Semestr 7 Programowanie współbieżne Zadanie 5 - Podstawowe problemy programowania współbieżnego
Wstęp Zadanie laboratoryjne polegało na implementacji problemu czytelników i pisarzy. Czytelnicy i pisarze rywalizują o dostęp do tego samego zasobu. W tym samym czasie z zasobu może korzystać dowolna liczba czytelników albo dokładnie jeden pisarz. Rozwiązanie W ramach rozwiązania został stworzony obiekt RWLock oferujący blokadę obiektu do czytania lub pisania. Zostały stworzone 3 różne implementacje: Rozwiązanie najprostsze Rozwiązanie to wykorzystuje jeden monitor oraz zamek powiązany z tym monitorem. Możliwa jest również implementacja w oparciu o dwa semafory. Czytelnicy zamykają zamek jedynie na czas rozpoczynania i kończenia czytania. Procedury te są sekcjami krytycznymi, a w ich czasie jest modyfikowany licznik czytelników. W wypadku pisarzy zamek jest zakładany w chwili rozpoczęcia pisania. Następnie dopóki istnieją czytelnicy zamek jest otwierany, zaś procesy zatrzymują się na monitorze w oczekiwaniu aż licznik czytelników zostanie wyzerowany. Po zakończeniu oczekiwania proces zamyka zamek i otwiera go po zakończeniu pisania. Rozwiązanie to jest poprawne (w sensie poprawnego ograniczenia dostępu do zasobu) oraz nie zawiera blokad. Nie rozwiązuje ono jednak w żaden sposób problemu zagłodzenia. Rozwiązanie z priorytetem dla pisarzy Do powyższego rozwiązania dodajemy licznik oczekujących pisarzy, który będzie zablokowany nowym zamkiem. Od tej chwili każdy czytelnik przed zwiększeniem licznika czytelników upewnia się, że żaden pisarz nie czeka. W przeciwnym wypadku czytelnik zatrzymuje się na monitorze do czasu zakończenia pracy wszystkich pisarzy. Rozwiązanie to rozbudowuje poprzednie rozwiązanie o optymalne wykorzystanie czasu. Dzięki temu do zagłodzenia dochodzi tylko w wypadku, gdy nowi pisarze pojawiają się szybiciej niż starzy kończą swoje zadania. Rozwiązanie z priorytetem dla oczekujących pisarzy Rozwiązaniem, które naszym zdaniem najlepiej pozbywa się problemu zagłodzenia jest modyfikacja powyższego. Tym razem priorytet jest przyznawany tym pisarzom, którzy czekali w trakcie czytania dowolnego czytelnika. W celu realizacji tej metody licznik pisarzy jest zwiększany dopiero po spełnieniu warunku zmiany licznika czytelników. Pewnym uproszczeniem w stosunku do poprzedniego rozwiązania jest fakt, że czytelnicy po otrzymaniu sygnału na monitorze o obsłużeniu wszystkich pisarzy z priorytetem nie upewniają się czy nie pojawił się nowy pisarz. Implementacja Rozwiązanie zostało wtkonane w języku Python, z wykorzystaniem biblioteki multiprocessing. W implementacji wykorzystywany jest fakt, iż logika monitorów znajduje się w klasie Condition odpowiadającej warunkowi monitora. Faktycznym elementem odpowiadającym monitorowi jest klasa Lock, której instancja jest parametrem konstruktora obiektu warunku. W PSK: Grzegorz Graczyk i Paweł Tarasiuk 2 / 9
efekcie implementacja zawiera jedynie jeden zamek co zmniejsza ryzyko popełnienia przez programistę błędu prowadzącego do blokady. Jako wątki występują czytelnicy i pisarze. Ponadto istnieje dodatkowy wątek służący genereowaniu wcześniej wymienionych za pomocą interfejsu okienkowego. W interfejsie okienkowym są widoczni wszyscy czytelnicy i pisarze wraz z informacją o stanie ich pracy. PSK: Grzegorz Graczyk i Paweł Tarasiuk 3 / 9
Kod programu Kod głównej części programu #! / usr / bin / env python # coding : utf 8 # c o n f i g : w r i t e r s p r i o r i t y = F a l s e c l i c k r e q u i r e d = True e n t r y t i m e = 1 working time = 1 l e a v i n g t i m e = 1 import pygtk pygtk. r e q u i r e ( 2. 0 ) import gtk, g o b j e c t from m u l t i p r o c e s s i n g import P r o c e s s as ThreadOrProcess, Queue, Semaphore from time import s l e e p i f w r i t e r s p r i o r i t y == True : p r i n t P r i o r y t e t p i s a r z y from prwlock import RWLock e l i f w r i t e r s p r i o r i t y == F a l s e : p r i n t Bez p r i o r y t e t u from rwlock import RWLock e l s e : p r i n t P r i o r y t e t oczekujacych p i s a r z y from xrwlock import RWLock d e f async ( f u n c t i o n ) : d e f C a l l ( args, kwargs ) : t = ThreadOrProcess ( t a r g e t=f u n c t i o n, a r g s=a r g s ) t. daemon = kwargs. get ( daemon, True ) t. s t a r t ( ) r e t u r n t r e t u r n C a l l l a b e l q u e u e = Queue ( ) r e s o u r c e = RWLock( ) @async d e f Reader ( i, s ) : l a b e l q u e u e. put ( ( i, C z y t e l n i k %d. [ ] % i ) ) s l e e p ( e n t r y t i m e ) r e s o u r c e. acquireread ( ) l a b e l q u e u e. put ( ( i, C z y t e l n i k %d. [ x ] % i ) ) s l e e p ( working time ) i f c l i c k r e q u i r e d : s. a c q u i r e ( ) r e s o u r c e. r e l e a s e R e a d ( ) l a b e l q u e u e. put ( ( i, < C z y t e l n i k %d. % i ) ) s l e e p ( l e a v i n g t i m e ) l a b e l q u e u e. put ( ( i, None ) ) @async d e f Writer ( i, s ) : l a b e l q u e u e. put ( ( i, [ ] P i s a r z %d. % i ) ) s l e e p ( e n t r y t i m e ) r e s o u r c e. a c q u i r e W r i t e ( ) l a b e l q u e u e. put ( ( i, [ x ] P i s a r z %d. % i ) ) s l e e p ( working time ) i f c l i c k r e q u i r e d : s. a c q u i r e ( ) r e s o u r c e. r e l e a s e W r i t e ( ) l a b e l q u e u e. put ( ( i, P i s a r z %d. > % i ) ) s l e e p ( l e a v i n g t i m e ) l a b e l q u e u e. put ( ( i, None ) ) PSK: Grzegorz Graczyk i Paweł Tarasiuk 4 / 9
################################# GUI ################################ c l a s s Window : d e f u p d a t e l a b e l s ( s e l f ) : w h i l e not l a b e l q u e u e. empty ( ) : i, l a b e l = l a b e l q u e u e. get ( ) i f l a b e l i s None : s e l f. buttons [ i ]. d e s t r o y ( ) e l s e : s e l f. buttons [ i ]. s e t l a b e l ( l a b e l ) g o b j e c t. timeout add ( 1 0, s e l f. u p d a t e l a b e l s ) d e f new button ( s e l f, box, text, t a s k ) : btn = gtk. Button ( Nowy %s % t e x t. lower ( ) ) btn. connect ( c l i c k e d, s e l f. n e w c l i c k e d, ( box, t a s k ) ) btn. s e t s i z e r e q u e s t ( 2 5 0, 5 0 ) box. pack end ( btn, False, F a l s e ) btn. show ( ) d e f button ( s e l f, box, s ) : btn = gtk. Button ( ) btn. connect ( c l i c k e d, s e l f. c l i c k e d, ( s ) ) btn. s e t s i z e r e q u e s t ( 2 5 0, 5 0 ) box. p a c k s t a r t ( btn, False, F a l s e ) btn. show ( ) r e t u r n btn d e f n e w c l i c k e d ( s e l f, widget, data=none ) : box, Task = data i = l e n ( s e l f. buttons ) s = Semaphore ( 0 ) s e l f. buttons += [ s e l f. button ( box, s ) ] Task ( i, s ) d e f c l i c k e d ( s e l f, widget, data=none ) : data. r e l e a s e ( ) d e f d e l e t e e v e n t ( s e l f, widget, event, data=none ) : r e t u r n F a l s e d e f d e s t r o y ( s e l f, widget, data=none ) : gtk. main quit ( ) d e f i n i t ( s e l f ) : s e l f. buttons = [ ] s e l f. window = gtk. Window( gtk.window TOPLEVEL) s e l f. window. connect ( d e l e t e e v e n t, s e l f. d e l e t e e v e n t ) s e l f. window. connect ( d e s t r o y, s e l f. d e s t r o y ) s e l f. window. s e t b o r d e r w i d t h ( 5 ) content = gtk. HBox ( ) l e f t = gtk. VBox ( ) l e f t. s e t b o r d e r w i d t h ( 5 ) s e l f. new button ( l e f t, C z y t e l n i k, Reader ) content. add ( l e f t ) l e f t. show ( ) sep = gtk. VSeparator ( ) content. add ( sep ) sep. show ( ) r i g h t = gtk. VBox ( ) r i g h t. s e t b o r d e r w i d t h ( 5 ) s e l f. new button ( r i g h t, P i s a r z, Writer ) content. add ( r i g h t ) r i g h t. show ( ) s e l f. window. add ( content ) PSK: Grzegorz Graczyk i Paweł Tarasiuk 5 / 9
content. show ( ) s e l f. window. show ( ) d e f main ( s e l f ) : g o b j e c t. timeout add (1000, s e l f. u p d a t e l a b e l s ) gtk. main ( ) Window ( ). main ( ) Kod rozwiązania najprostszego from m u l t i p r o c e s s i n g import Lock, Value, Condition c l a s s RWLock : d e f i n i t ( s e l f ) : s e l f. l o c k = Lock ( ) s e l f. c o n d i t i o n = Condition ( s e l f. l o c k ) s e l f. r e a d e r s = Value ( i, 0, l o c k=f a l s e ) d e f acquireread ( s e l f ) : s e l f. r e a d e r s. value += 1 d e f a c q u i r e W r i t e ( s e l f ) : w h i l e s e l f. r e a d e r s. value > 0 : s e l f. c o n d i t i o n. wait ( ) d e f r e l e a s e R e a d ( s e l f ) : s e l f. r e a d e r s. value = 1 i f s e l f. r e a d e r s. value == 0 : s e l f. c o n d i t i o n. n o t i f y a l l ( ) d e f r e l e a s e W r i t e ( s e l f ) : Kod rozwiązania z priorytetem czytelników from m u l t i p r o c e s s i n g import Lock, Value, Condition c l a s s RWLock : d e f i n i t ( s e l f ) : s e l f. l o c k = Lock ( ) s e l f. c o n d i t i o n = Condition ( s e l f. l o c k ) s e l f. r e a d e r s = Value ( i, 0, l o c k=f a l s e ) s e l f. wcondition = Condition ( s e l f. l o c k ) s e l f. w r i t e r s = Value ( i, 0) d e f acquireread ( s e l f ) : w h i l e s e l f. w r i t e r s. value > 0 : s e l f. wcondition. wait ( ) s e l f. r e a d e r s. value += 1 d e f a c q u i r e W r i t e ( s e l f ) : s e l f. w r i t e r s. value += 1 w h i l e s e l f. r e a d e r s. value > 0 : s e l f. c o n d i t i o n. wait ( ) d e f r e l e a s e R e a d ( s e l f ) : s e l f. r e a d e r s. value = 1 i f s e l f. r e a d e r s. value == 0 : s e l f. c o n d i t i o n. n o t i f y a l l ( ) PSK: Grzegorz Graczyk i Paweł Tarasiuk 6 / 9
d e f r e l e a s e W r i t e ( s e l f ) : s e l f. w r i t e r s. value = 1 i f s e l f. w r i t e r s. value == 0 : s e l f. wcondition. n o t i f y a l l ( ) Kod rozwiązania z priorytetem oczekujących czytelników from m u l t i p r o c e s s i n g import Lock, Value, Condition c l a s s RWLock : d e f i n i t ( s e l f ) : s e l f. l o c k = Lock ( ) s e l f. c o n d i t i o n = Condition ( s e l f. l o c k ) s e l f. r c o n d i t i o n = Condition ( s e l f. l o c k ) s e l f. r e a d e r s = Value ( i, 0, l o c k=f a l s e ) s e l f. f i r s t r e a d e r = Value ( f, 0, l o c k=f a l s e ) s e l f. w r i t e r s = Value ( i, 0, l o c k=f a l s e ) d e f acquireread ( s e l f ) : i f s e l f. w r i t e r s : s e l f. r c o n d i t i o n. wait ( ) s e l f. r e a d e r s. value += 1 d e f a c q u i r e W r i t e ( s e l f ) : f o r c i n g = F a l s e w h i l e s e l f. r e a d e r s. value > 0 : s e l f. c o n d i t i o n. wait ( ) i f not f o r c i n g : s e l f. w r i t e r s. value += 1 f o r c i n g = True i f f o r c i n g : s e l f. w r i t e r s. value = 1 i f s e l f. w r i t e r s. value == 0 : s e l f. r c o n d i t i o n. n o t i f y a l l ( ) d e f r e l e a s e R e a d ( s e l f ) : s e l f. r e a d e r s. value = 1 s e l f. c o n d i t i o n. n o t i f y a l l ( ) d e f r e l e a s e W r i t e ( s e l f ) : Diagramy Za pomocą kolorów oznaczono sekcje krytyczne. PSK: Grzegorz Graczyk i Paweł Tarasiuk 7 / 9
Rozwiązanie najprostsze Rozwiązanie z priorytetem czytelników PSK: Grzegorz Graczyk i Paweł Tarasiuk 8 / 9
Rozwiązanie z priorytetem oczekujących czytelników PSK: Grzegorz Graczyk i Paweł Tarasiuk 9 / 9