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

Podobne dokumenty