Kurs języka Python Wątki
Wątek Wątek (ang. thread) to jednostka wykonawcza w obrębie jednego procesu, będąca kolejnym ciągiem instrukcji wykonywanym w obrębie tych samych danych (w tej samej przestrzeni adresowej). Wątki tego samego procesu korzystają ze wspólnego kodu i danych, mają jednak oddzielne stosy. Źródło: www.wikipedia.pl
Do czego używa się wątków Serwery (np. WWW), które obsługujące wielu klientów Obsługa systemów GUI; zlecenie wykonania zadania nie powoduje 'zamrożenia' działania interfejsu
Wątki, przykład MASZYNA WIRTUALNA i = i + 1 print i i = i + 1 print i i = i + 1 i = i - 1 print i i = i - 1 print i i = i - 1 print i print i
Przykładowy przebieg MASZYNA WIRTUALNA i = i + 1 print i i = i + 1 print i i = i + 1 i = i - 1 print i i = i - 1 print i i = i - 1 print i print i
Przykładowy przebieg MASZYNA WIRTUALNA i = i + 1 print i i = i + 1 print i i = i + 1 i = i - 1 print i i = i - 1 print i i = i - 1 print i print i
Moduł threading class Thread: def start(self): '''Wywołuje metodę run''' def run(self): pass
Zadanie Zasymulowanie za pomocą wątków biegaczy w maratonie.
Implementacja biegaczy import threading total_distance = 0 class runner(threading.thread): def init (self, nr_startowy): self.numer = nr_startowy threading.thread. init (self)
Implementacja biegu def run(self): global total_distance dystans = 42195 while dystans > 0: dystans = dystans - 1 total_distance = total_distance + 1 if dystans % 10000 == 0: print 'Zawodnik nr %i' % self.numer print 'Zawodnik %i na mecie' % self.numer
Rozpoczęcie wyścigu r1 = runner(1) r2 = runner(2) r1.start() r2.start() r1.join() r2.join() print 'koniec wyścigu, dystans', total_distance
.join() 'Główny' program to też wątek, to oznacza że po wywołaniu r1.start() są DWA wątki w.join() powoduje czekanie na zakończenie wątku w
Dostęp do wspólnej zmiennej total_distance = 0 class runner(threading.thread):... total_distance = total_distance + 1 print total_distance
Zagadka Jaka jest wartość zmiennej total_distance?
Operacje 'atomowe'? i = i + 1 Wątek 1: LOADFAST 0 LOAD_CONST 1 BINARY_ADD STORE_FAST 0 Wątek 2: LOADFAST 0 LOAD_CONST 1 BINARY_ADD STORE_FAST 0
Blokady Lock lock = Lock() def run(self): global lock... lock.acquire() total_distance = total_distance + 1 lock.release()
Lock Szybko działa Możliwość samozakleszczenia: lock.acquire() lock.acquire()
Blokady wielowchodliwe (reentrant) lock = threading.rlock() lock.acquire()... lock.acquire() i = i + 1 lock.release()... lock.release()
RLock Aby zwolnienić blokadę trzeba zwolnić ją tyle razy ile została założona Program jest wyraźnie wolniejszy od programu z blokadami typu Lock
Modyfikacja zadania sztafeta Każdy biegacz ma zmienną 'pałeczka', która ma wartość 0 lub 1 Jeśli pałeczka == 0, to biegacz musi czekać w miejscu, aż mu ją ktoś przekaże, tj. ustawi paleczka == 1
Pierwsze podejście Posiadacz = 1 # zmienna globalna # aktywne czekanie, wątek nr. n while posiadacz!= n: pass
Synchronizacja wątków class Biegacz(threading.Thread): def init (self, nr_startowy, blokada, nastepny=none, paleczka=0): self.numer = nr_startowy self.blokada = blokada self.paleczka = paleczka self.nastepny = nastepny threading.thread. init (self)
def run(self): dystans = 40000 self.blokada.acquire() while self.paleczka == 0: self.blokada.wait() print "%i wystartowal" % self.numer while dystans > 0: dystans = dystans - 1
if self.nastepny: self.paleczka = 0 self.nastepny.paleczka = 1 self.blokada.notifyall() self.blokada.release() print "zawodnik %i dobiegl" % self.numer
lock = threading.condition() r4 = Biegacz(4, lock) r3 = Biegacz(3, lock, r4) r2 = Biegacz(2, lock, r3) r1 = Biegacz(1, lock, r2, 1) for r in [r4, r3, r2, r1]: r.start()
Mechanizmy wake() - zwalnia uzyskaną blokadę i 'zasypia' dopóki nie zostanie obudzony. Po obudzeniu otrzymuje blokadę notify() - 'budzi' inny wątek uśpiony przy tej blokadzie metodą wake notifyall() - budzi wszystkie wątki, które przysnęły przy tej blokadzie
Komunikacja między wątkami skrzynka = '' # nadawca lock.acquire() while len(skrzynka) > 0: lock.wait() skrzynka = 'komunikat' lock.notify() lock.release()
Odbiorca lock.acquire() while len(skrzynka) == 0: skrzynka.wait() print skrzynka skrzynka = '' lock.notify() lock.release()
Wady Nie można zbuforować więcej niż jeden komunikat Jeśli dwa wątki pracują z różną wydajnością, to szybszy będzie ciągle spowalniany
Dostęp do struktur danych Zwykle struktury danych nie są bezpieczne ze względu na wątki (thread-safe)
Klasa Queue Kolejka FIFO przechowująca obiekty Bezpieczna ze względu na wątki
Metody Konstruktor: Queue(rozmiar) put(item) get(item)
Zastosowanie praktyczne Testowanie wydajności serwera WWW zalewając go odwołaniami do strony
Architektura Scenariusze (lista kolejnych odwołań), np: [ "/", "/cms/pl/about_institute", "/"] Równoległe uruchomienie wielu tych samych scenariuszy ROZWIĄZANIE Implementacja własnej miniprzeglądarki działającej jako wewnątrz własnego wątka Implementacja klasy HttpBrowser
Implementacja class HttpBrowser(threading.Thread): def init (self, connection, host, scenariusz): def run(self):
host = "www.ii.uni.wroc.pl" scenariusz = [ "/", "/cms/pl/about_institute", "/"] agenty = [ HttpBrowser( httplib.httpconnection, host, scenariusz) for a in range(20) ] for a in agenty: a.start()
Najbliższa przyszłość Sieci (protokoły http, SOAP etc.) Jeszcze o Pythonie (m.in. refleksje) Współpraca z innymi językami (C, Java, Mono) Narzędzia developerskie (testowanie, debuggowanie, profilowanie)