Programowanie współbieżne Zadanie 5
Transkrypt
Programowanie współbieżne Zadanie 5
150875 151021 numer indeksu Grzegorz Graczyk imię i nazwisko Data Kierunek Specjalizacja Rok akademicki Semestr numer indeksu Paweł Tarasiuk imię i nazwisko 2011-11-07 Informatyka Inżynieria Oprgoramowania i Analiza Danych 2011/12 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 #! / u s r / b i n / env python # −∗− c o d i n g : u t f −8 −∗− # config : writers priority click required entry time working time leaving time = = = = = False True 1 1 1 im po rt pygtk pygtk . r e q u i r e ( ’ 2 . 0 ’ ) im po rt gtk , g o b j e c t from m u l t i p r o c e s s i n g im por t P r o c e s s a s ThreadOrProcess , Queue , Semaphore from time i mpo rt s l e e p w r i t e r s p r i o r i t y == True : print ’ Priorytet pisarzy ’ from p r w l o c k imp ort 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 r w l o c k i mpo rt RWLock else : print ’ Priorytet oczekujacych pisarzy ’ from x r w l o c k im po rt RWLock if def async ( f u n c t i o n ) : d e f C a l l ( ∗ a r g s , ∗ ∗ 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 . g e t ( ’ daemon ’ , True ) t . start () return t return Call 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 ) ) sleep ( entry time ) 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 ) if click required : s . acquire () resource . releaseRead () l a b e l q u e u e . put ( ( i , ”<− C z y t e l n i k %d . ” % i ) ) sleep ( leaving time ) l a b e l q u e u e . put ( ( i , None ) ) @async def Writer ( i , s ) : l a b e l q u e u e . put ( ( i , ” [ ] P i s a r z %d . ” % i ) ) sleep ( entry time ) resource . acquireWrite () 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 ) if click required : s . acquire () resource . releaseWrite () l a b e l q u e u e . put ( ( i , ” P i s a r z %d . −>” % i ) ) sleep ( leaving time ) 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 : def update labels ( 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 = label queue . get () i f l a b e l i s None : s e l f . buttons [ i ] . destroy ( ) else : s e l f . buttons [ i ] . s e t l a b e l ( l a b e l ) gobject . timeout add (10 , 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 , t e x t , t a s k ) : btn = g t k . Button ( ”Nowy %s ” % t e x t . l o w e r ( ) ) btn . c o n n e c t ( ” 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 . p a c k e n d ( btn , F a l s e , F a l s e ) btn . show ( ) d e f b ut to n ( s e l f , box , s ) : btn = g t k . Button ( ) btn . c o n n e c t ( ” 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 , F a l s e , 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 = len ( s e l f . buttons ) s = Semaphore ( 0 ) s e l f . b u t t o n s += [ s e l f . but t on ( 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 ) : return False d e f d e s t r o y ( s e l f , widget , data=None ) : gtk . main quit ( ) def init ( self ): s e l f . buttons = [ ] s e l f . window = g t k . Window ( g t k .WINDOW TOPLEVEL) s e l f . window . c o n n e c t ( ” 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 . c o n n e c t ( ” 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 ) c o n t e n t = g t k . HBox ( ) l e f t = g t k . VBox ( ) l e f t . set border width (5) s e l f . new button ( l e f t , ” C z y t e l n i k ” , Reader ) c o n t e n t . add ( l e f t ) l e f t . show ( ) s e p = g t k . V Separ ator ( ) c o n t e n t . add ( s e p ) s e p . show ( ) r i g h t = g t k . VBox ( ) right . set border width (5) s e l f . new button ( r i g h t , ” P i s a r z ” , W r i t e r ) c o n t e n t . add ( r i g h t ) r i g h t . show ( ) s e l f . window . add ( c o n t e n t ) PSK: Grzegorz Graczyk i Paweł Tarasiuk 5/9 c o n t e n t . show ( ) s e l f . window . show ( ) d e f main ( s e l f ) : gobject . timeout add (1000 , s e l f . u p d a t e l a b e l s ) g t k . main ( ) Window ( ) . main ( ) Kod rozwiązania najprostszego from m u l t i p r o c e s s i n g im por t Lock , Value , C o n d i t i o n c l a s s RWLock : def init ( self ): s e l f . l o c k = Lock ( ) s e l f . condition = Condition ( s e l f . lock ) s e l f . r e a d e r s = Value ( ’ i ’ , 0 , l o c k=F a l s e ) def acquireRead ( s e l f ) : s e l f . lock . acquire () s e l f . r e a d e r s . v a l u e += 1 s e l f . lock . release () def acquireWrite ( s e l f ) : s e l f . lock . acquire () while s e l f . readers . value > 0: s e l f . c o n d i t i o n . wait ( ) def releaseRead ( s e l f ) : s e l f . lock . acquire () s e l f . r e a d e r s . v a l u e −= 1 i f s e l f . r e a d e r s . v a l u e == 0 : s e l f . condition . n o t i f y a l l () s e l f . lock . release () def releaseWrite ( s e l f ) : s e l f . lock . release () Kod rozwiązania z priorytetem czytelników from m u l t i p r o c e s s i n g im por t Lock , Value , C o n d i t i o n c l a s s RWLock : def init ( self ): s e l f . l o c k = Lock ( ) s e l f . condition = Condition ( s e l f . lock ) 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 . lock ) s e l f . w r i t e r s = Value ( ’ i ’ , 0 ) def acquireRead ( s e l f ) : s e l f . lock . acquire () while s e l f . writers . value > 0: s e l f . wcondition . wait ( ) s e l f . r e a d e r s . v a l u e += 1 s e l f . lock . release () def acquireWrite ( s e l f ) : s e l f . w r i t e r s . v a l u e += 1 s e l f . lock . acquire () while s e l f . readers . value > 0: s e l f . c o n d i t i o n . wait ( ) def releaseRead ( s e l f ) : s e l f . lock . acquire () s e l f . r e a d e r s . v a l u e −= 1 i f s e l f . r e a d e r s . v a l u e == 0 : s e l f . condition . n o t i f y a l l () PSK: Grzegorz Graczyk i Paweł Tarasiuk 6/9 s e l f . lock . release () def releaseWrite ( s e l f ) : s e l f . w r i t e r s . v a l u e −= 1 i f s e l f . w r i t e r s . v a l u e == 0 : s e l f . wcondition . n o t i f y a l l () s e l f . lock . release () 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 im por t Lock , Value , C o n d i t i o n c l a s s RWLock : init ( self ): def s e l f . l o c k = Lock ( ) s e l f . condition = Condition ( s e l f . lock ) s e l f . r c o n d i t i o n = Condition ( s e l f . lock ) 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 ) def acquireRead ( s e l f ) : s e l f . lock . acquire () i f s e l f . writers : 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 . v a l u e += 1 s e l f . lock . release () def acquireWrite ( s e l f ) : s e l f . lock . acquire () forcing = False while s e l f . readers . 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 . v a l u e += 1 f o r c i n g = True i f forcing : s e l f . w r i t e r s . v a l u e −= 1 i f s e l f . w r i t e r s . v a l u e == 0 : s e l f . rcondition . n o t i f y a l l () def releaseRead ( s e l f ) : s e l f . lock . acquire () s e l f . r e a d e r s . v a l u e −= 1 s e l f . condition . n o t i f y a l l () s e l f . lock . release () def releaseWrite ( s e l f ) : s e l f . lock . release () 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