Wykład
3
Wątki w Pythonie
Na podstawie:
Wykładu Marcina Młotkowskiego
oraz
[Link]
oraz
[Link]
1
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.
Po co używać wątki
zrównoleglenie wolnych operacji wejścia/wyjścia (ściąganie pliku/obsługa interfejsu)
jednoczesna obsługa wielu operacji, np. serwery WWW
zrównoleglenie czasochłonnych obliczeń numerycznych
2
Prrzykład
dowe ob
bliczeniia programu w
wielowąątkowego
3
Wątki w Pythonie. Moduły
thread (3.0: _thread): niskopoziomowa biblioteka
threading: wysokopoziomowa biblioteka
dummy_thread
Udostępnia duplikat interfejsu do modułu thread. Importowany wtedy gdy moduł tread nie jest
dostępny na platformie
Sugestia zastosowania:
try:
import thread as _thread
except ImportError:
import dummy_thread as _thread
dummy_threading – odpowiednik dummy_thread ale dla modułu threading
4
Wątki w Pythonie. Moduły c.d.
multiprocessing
Pakiet, który wspiera procesy zarządzania wątkami z wykorzystaniem interfejsu API podobnego do
modułu threading. Pakiet multiprocessing oferuje zarówno lokalną i zdalną współbieżność,
skutecznie omijając GIL (Global Interpreter Lock) używając podprocesów zamiast wątków. Ze
względu na to, moduł multiprocessing pozwala programiście w pełni wykorzystać wiele
procesorów na danym komputerze. Działa na systemach Unix i Windows.
5
Moduł thread
Aby rozpocząć nowy wątek:
thread.start_new_thread ( function, args[, kwargs] )
Metoda umożliwia tworzenie nowego wątku w Linux i Windows
Metoda startuje nowy wątek, który rozpoczyna funkcję z argumentami przekazanymi w liście args.
Kiedy funkcja kończy działanie wątek również kończy działanie. args reprezentuje krotkę
argumentów. Można przekazać pustą krotkę aby wywołać funkcję bez argumentów, kwargs to
opcjonalny słownik nazw argumentów.
6
Przykład
#!/usr/bin/python
import thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
[Link](delay)
count += 1
print "%s: %s" % ( threadName, [Link]([Link]()))
# Create two threads as follows
try:
thread.start_new_thread( print_time, ("Thread-1", 2, ) )
thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print "Error: unable to start thread"
while 1:
pass
7
Wynik działania
Thread-1: Thu Jan 22 [Link] 2009
Thread-1: Thu Jan 22 [Link] 2009
Thread-2: Thu Jan 22 [Link] 2009
Thread-1: Thu Jan 22 [Link] 2009
Thread-2: Thu Jan 22 [Link] 2009
Thread-1: Thu Jan 22 [Link] 2009
Thread-1: Thu Jan 22 [Link] 2009
Thread-2: Thu Jan 22 [Link] 2009
Thread-2: Thu Jan 22 [Link] 2009
Thread-2: Thu Jan 22 [Link] 2009
Moduł bardzo efektywny kiedy wystarczy niskopoziomowe programowanie wątków. Znacznie
bardziej użyteczny jest moduł threading wprowadzony w Python 2.4. umożliwia wysokopoziomowe
zarządzanie wątkami.
8
Moduł threading
Moduł threading
class Thread:
def run(self):
"""Operacje wykonywane w watku"""
def start(self):
"""Wystartowanie obliczen w watku"""
9
Przykład
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread ([Link]):
def __init__(self, threadID, name, counter):
[Link].__init__(self)
[Link] = threadID
[Link] = name
[Link] = counter
def run(self):
print "Starting " + [Link]
print_time([Link], [Link], 5)
print "Exiting " + [Link]
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
[Link]()
[Link](delay)
10
print "%s: %s" % (threadName, [Link]([Link]()))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
[Link]()
[Link]()
print "Exiting Main Thread"
Rezultat działania
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 [Link] 2013
Thread-1: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Thread-1: Thu Mar 21 [Link] 2013
Thread-1: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
11
Thread-1: Thu Mar 21 [Link] 2013
Exiting Thread-1
Thread-2: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Exiting Thread-2
12
Maraton (przykład)
import threading
Rozpoczęcie biegu:
total_distance = 0
r1 = runner(1)
class runner([Link]):
r2 = runner(2)
def __init__(self, nr_startowy):
[Link]()
[Link] = nr_startowy
[Link]()
[Link].__init__(self)
[Link]()
def run(self):
[Link]()
global total_distance
print ’koniec wyscigu, dystans’,
dystans = 42195
total_distance
while dystans > 0:
dystans = dystans - 1
total_distance = total_distance + 1
if dystans % 10000 == 0:
print ’Zawodnik nr %i’ % [Link]
print ’Zawodnik %i na mecie’ % [Link]
13
Maraton c.d
Rozpoczęcie biegu:
r1 = runner(1)
r2 = runner(2)
[Link]()
[Link]()
[Link]()
[Link]()
print ’koniec wyscigu, dystans’, total_distance
Główny program to też wątek, więc po wywołaniu [Link]() są dwa wątki
[Link]() oznacza, ze wątek nadrzędny będzie czekał na zakończenie wątku r1
14
Dostęp do wspó lnej zmiennej wątkó w
total_distance = 0
class runner([Link]):
...
total_distance = total_distance + 1
print total_distance
Jaka jest wartość zmiennej total_distance?
Powinno być: 2 * 42195 = 84390
A jest:
54380
73300
83460
15
Operacje atomowe
i=i+1
LOADFAST 0
LOAD_CONST 1
BINARY_ADD
STORE_FAST 0
16
Blokady
Nowa blokada jest tworzona poprzez wywołanie funkcji Lock() która zwraca nowy obiekt blokadę.
Metoda acquire(blocking=1) obiektu blokady umożliwia synchronizowanie wątków.
Jeżeli parameter blocking ustawiony jest na 0 wątek kończy działanie z wartością 0 jeżeli nie można
założyć blokady oraz zwraca 1 kiedy blokada została założona. Jeżeli blocking jest ustawiony na 1
wątek zakłada blokadę.
Można użyć metody release() do zwolnienia blokady.
lock = Lock()
def run(self):
global lock
...
[Link]()
total_distance = total_distance + 1
[Link]()
17
Blokady c.d.
Blokada RLock: Wątek może założyć blokadę dowolną liczbę razy, i tyleż razy musi ją zwolnić.
Bardzo spowalnia program.
Blokada Semaphore: Blokadę można założyć ustaloną liczbę razy.
sem = Semaphore(3)
[Link]()
[Link]()
[Link]()
[Link]() # blokada
18
Czekanie na zasó b
Jeden wątek (barman) nalewa mleko do szklanki, drugi (klient) czeka na napełnienie szklanki do
pełna i wypija mleko.
lck = Lock()
# nalewanie
[Link]()
for i in range(5):
szklanka_mleka = szklanka_mleka + 1
[Link]()
#wypijanie
while szklanka_mleka ! = 5: pass
[Link]()
while szklanka_mleka > 0:
szklanka_mleka = szklanka_mleka - 1
[Link]()
19
kolejny przykład
#!/usr/bin/python
import threading
import time
class myThread ([Link]):
def __init__(self, threadID, name, counter):
[Link].__init__(self)
[Link] = threadID
[Link] = name
[Link] = counter
def run(self):
print "Starting " + [Link]
# Get lock to synchronize threads
[Link]()
print_time([Link], [Link], 3)
# Free lock to release next thread
[Link]()
def print_time(threadName, delay, counter):
while counter:
20
[Link](delay)
print "%s: %s" % (threadName, [Link]([Link]()))
counter -= 1
threadLock = [Link]()
threads = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
[Link]()
[Link]()
# Add threads to thread list
[Link](thread1)
[Link](thread2)
# Wait for all threads to complete
for t in threads:
[Link]()
print "Exiting Main Thread"
21
Wynik działania programu
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 [Link] 2013
Thread-1: Thu Mar 21 [Link] 2013
Thread-1: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Thread-2: Thu Mar 21 [Link] 2013
Exiting Main Thread
22
Usypianie i budzenie wątkó w
lck = [Link]()
# Wypijanie mleka
[Link]()
while szklanka_mleka != 5:
[Link]() #Usypianie
while szklanka_mleka > 0: szklanka_mleka = szklanka_mleka - 1
[Link]()
#Nalewanie mleka
[Link]()
for i in range(5):
szklanka_mleka = szklanka_mleka + 1
[Link]() #budzenie
[Link]()
23
Zmienne warunkowe
Zmienne warunkowe są zmiennymi działającymi jak blokady (aquire(), release());
metoda wait() zwalnia blokadę i usypia bieżący wątek;
metoda notify() budzi jeden z uśpionych wątków (na tej zmiennej warunkowej), notifyAll()
budzi wszystkie uśpione wątki.
Wady mechanizmu
Można nalewać i pić tylko z jednej szklanki.
Barman nie może nalać wiele szklanek na zapas i iść do domu.
24
Wielowątkowa kolejka priorytetó w
Moduł Queue umożliwia tworzenie obiektu kolejki, która może przechować określoną liczbę
elementów.
Poniżej opisano metody zarządzające obiektem Queue:
get() usuwa i zwraca obiekt z kolejki.
put() Dodaje element do kolejki.
qsize() Zwraca liczbę elementów w kolejce.
empty() Zwraca True gdy obiekt jest pusty. W przeciwnym wypadku False.
full() Zwraca True kiedy kolejka jest pełna. False w przeciwnym wypadku
25
Przykład
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread ([Link]):
def __init__(self, threadID, name, q):
[Link].__init__(self)
[Link] = threadID
[Link] = name
self.q = q
def run(self):
print "Starting " + [Link]
process_data([Link], self.q)
print "Exiting " + [Link]
def process_data(threadName, q):
while not exitFlag:
[Link]()
if not [Link]():
data = [Link]()
26
[Link]()
print "%s processing %s" % (threadName, data)
else:
[Link]()
[Link](1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = [Link]()
workQueue = [Link](10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
[Link]()
[Link](thread)
threadID += 1
# Fill the queue
[Link]()
for word in nameList:
[Link](word)
[Link]()
27
# Wait for queue to empty
while not [Link]():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
[Link]()
print "Exiting Main Thread"
Wynik działania programu
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
28
Exiting Thread-2
Exiting Main Thread
29