150875
numer indeksu Grzegorz Graczyk
imię i nazwisko
151021
numer indeksu Paweł Tarasiuk
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ła stworzona klasa RWLock, która posiada metody pozwalające oddzielić kod samych czytelników i pisarzy od samej implementacji rozwiązania problemu. Dzięki temu raz napisany program testujący może być testowany na różnych implementacjach.
Przygotowaliśmy trzy propozycje rozwiązania problemu (czyli odmienne implementacje klasy RWLock):
Rozwiązanie wstępne
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 chwi- li 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 za- koń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.
PSK: Grzegorz Graczyk i Paweł Tarasiuk 2 / 9
Rozwiązanie z priorytetem dla pisarzy
Do powyższego rozwiązania dodajemy licznik oczekujących pisarzy, który będzie zabloko- wany 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ę szybciej niż starzy kończą swoje zadania.
PSK: Grzegorz Graczyk i Paweł Tarasiuk 3 / 9
Rozwiązanie z priorytetem dla oczekujących pisarzy
Rozwiązaniem, które naszym zdaniem najlepiej pozbywa się problemu zagłodzenia jest mody- fikacja powyższego. Tym razem priorytet jest przyznawany tym pisarzom, którzy czekali w trak- cie 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 stosun- ku 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.
PSK: Grzegorz Graczyk i Paweł Tarasiuk 4 / 9
Implementacja
Rozwiązania zostały wykonane w języku Python, z wykorzystaniem biblioteki multipro- cessing. W implementacji wykorzystywany jest fakt, iż logika monitorów znajduje się w kla- sie Condition odpowiadającej warunkowi monitora. Faktycznym elementem odpowiadającym monitorowi jest klasa Lock, której instancja jest parametrem konstruktora obiektu warunku.
W efekcie implementacja zawiera jedynie jeden zamek co zmniejsza ryzyko popełnienia przez programistę błędu prowadzącego do blokady.
Wątkami są czytelnicy oraz pisarze. Ponadto istnieje dodatkowy wątek służący tworzeniu czytelników i pisarzy za pomocą interfejsu graficznego. Interfejs pozwala śledzić stan czytelników i pisarzy - zmiany w tym stanie świadczą o tym, komu po kolei przydzielany jest zasób.
Interfejs okienkowy został wykonany w oparciu o bidingi biblioteki gtk dla języka Python.
Podsumowanie
Problem czytelników i pisarzy można rozwiązywać na wiele sposobów, a ocena które z tych sposobów są lepsze nie jest uniwersalna - wszystko zależy od oczekiwań co do priorytetu pomię- dzy czytelnikami a pisarzami. Przygotowane przez nas rozwiązanie z priorytetem dla pisarzy jest popularnym podejściem do problemu, lecz pomysł będący podstawą trzeciej spośród przygotowa- nych przez nas propozycji rozwiązania stanowi pewien kompromis pomiędzy znaczeniem pisarzy (którzy powinni jak najszybciej aktualizować zasób), a unikaniem zagłodzenia czytelników.
PSK: Grzegorz Graczyk i Paweł Tarasiuk 5 / 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 −∗−
# 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
w o r k i n g t i m e = 1
l e a v i n g t i m e = 1
i m p o r t p yg tk
p yg tk . r e q u i r e (’ 2 . 0 ’) i m p o r t gtk , g o b j e c t
from m u l t i p r o c e s s i n g i m p o r t P r o c e s s a s Th re a dO rP r oc es s , Queue , Semaphore from t i m e i m p o r t 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 p r w l o c k i m p o r t 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 m p o r t RWLock e l s e:
p r i n t ’ P r i o r y t e t o c z e k u j a c y c h p i s a r z y ’ from x r w l o c k i m p o r t RWLock
d e f a s y n c ( f u n c t i o n ) :
d e f C a l l ( ∗ a r g s , ∗ ∗ kwargs ) :
t = T h r e a d O r P r o c e s s ( 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 . 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 . a c q u i r 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 . [ x ] ” % i ) ) s l e e p ( w o r k i n g t i m e )
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 W r i t e r ( 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 ( w o r k i n g t i m e )
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 6 / 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 n o t 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 . g e t ( ) i f l a b e l i s None :
s e l f . b u t t o n s [ i ] . d e s t r o y ( ) e l s e:
s e l f . b u t t o n s [ i ] . s e t l a b e l ( l a b e l ) g o b j e c t . t i m e o u t a d d ( 1 0 , s e l f . u p d a t e l a b e l s )
d e f n e w b u t t o n ( 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 u t t o 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 , w i d g e t , d a t a=None ) : box , Task = d a t a
i = l e n ( s e l f . b u t t o n s ) s = Semaphore ( 0 )
s e l f . b u t t o n s += [ s e l f . b u t t o n ( box , s ) ] Task ( i , s )
d e f c l i c k e d ( s e l f , w i d g e t , d a t a=None ) : d a t a . 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 , w i d g e t , e v e n t , d a t a=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 , w i d g e t , d a t a=None ) : g t k . m a i n q u i t ( )
d e f i n i t ( s e l f ) : s e l f . b u t t o n s = [ ]
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 . s e t b o r d e r w i d t h ( 5 )
s e l f . n e w b u t t o n ( 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 S e p a r a t o r ( ) c o n t e n t . add ( s e p ) s e p . show ( )
r i g h t = g t k . 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 . n e w b u t t o n ( 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 7 / 9
c o n t e n t . show ( ) s e l f . window . show ( )
d e f main ( s e l f ) :
g o b j e c t . t i m e o u t a d d ( 1 0 0 0 , 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 i m p o r t Lock , Value , C o n d i t i o n
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 = C o n d i t i o n ( 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 a c q u i r e R e a d ( s e l f ) : s e l f . l o c k . a c q u i r e ( ) s e l f . r e a d e r s . v a l u e += 1 s e l f . l o c k . r e l e a s e ( )
d e f a c q u i r e W r i t e ( s e l f ) : s e l f . l o c k . a c q u i r e ( )
w h i l e s e l f . r e a d e r s . v a l u e > 0 : s e l f . c o n d i t i o n . w a i t ( )
d e f r e l e a s e R e a d ( s e l f ) : s e l f . l o c k . a c q u i r e ( ) 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 . c o n d i t i o n . n o t i f y a l l ( ) s e l f . l o c k . r e l e a s e ( )
d e f r e l e a s e W r i t e ( s e l f ) : s e l f . l o c k . r e l e a s e ( )
Kod rozwiązania z priorytetem czytelników
from m u l t i p r o c e s s i n g i m p o r t Lock , Value , C o n d i t i o n
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 = C o n d i t i o n ( 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 . w c o n d i t i o n = C o n d i t i o n ( 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 a c q u i r e R e a d ( s e l f ) : s e l f . l o c k . a c q u i r e ( )
w h i l e s e l f . w r i t e r s . v a l u e > 0 : s e l f . w c o n d i t i o n . w a i t ( ) s e l f . r e a d e r s . v a l u e += 1 s e l f . l o c k . r e l e a s e ( )
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 . v a l u e += 1 s e l f . l o c k . a c q u i r e ( )
w h i l e s e l f . r e a d e r s . v a l u e > 0 : s e l f . c o n d i t i o n . w a i t ( )
d e f r e l e a s e R e a d ( s e l f ) : s e l f . l o c k . a c q u i r e ( ) 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 . c o n d i t i o n . n o t i f y a l l ( )
PSK: Grzegorz Graczyk i Paweł Tarasiuk 8 / 9
s e l f . l o c k . r e l e a s e ( )
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 . 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 . w c o n d i t i o n . n o t i f y a l l ( ) s e l f . l o c k . r e l e a s e ( )
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 i m p o r t Lock , Value , C o n d i t i o n
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 = C o n d i t i o n ( s e l f . l o c k ) s e l f . r c o n d i t i o n = C o n d i t i o n ( 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 a c q u i r e R e a d ( s e l f ) : s e l f . l o c k . a c q u i r e ( ) 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 . w a i t ( ) s e l f . r e a d e r s . v a l u e += 1 s e l f . l o c k . r e l e a s e ( )
d e f a c q u i r e W r i t e ( s e l f ) : s e l f . l o c k . a c q u i r e ( ) 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 . v a l u e > 0 : s e l f . c o n d i t i o n . w a i t ( ) i f n o t 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 f o r c i n g :
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 . 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 . l o c k . a c q u i r e ( ) s e l f . r e a d e r s . v a l u e −= 1 s e l f . c o n d i t i o n . n o t i f y a l l ( ) s e l f . l o c k . r e l e a s e ( )
d e f r e l e a s e W r i t e ( s e l f ) : s e l f . l o c k . r e l e a s e ( )