You are on page 1of 29

Wykład

3
Wątki w Pythonie
Na podstawie: 
Wykładu Marcina Młotkowskiego 
oraz 
http://docs.python.org

oraz
http://www.tutorialspoint.com/python/python_multithreading.htm

 
   


 
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


 
Prrzykład
dowe ob
bliczeniia programu w
wielowąątkowego


 
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


 
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.
 


 
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.
 


 
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:
time.sleep(delay)
count += 1
print "%s: %s" % ( threadName, time.ctime(time.time()))

# 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
 


 
Wynik działania
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 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.


 
Moduł threading
Moduł threading

class Thread:
def run(self):
"""Operacje wykonywane w watku"""
def start(self):
"""Wystartowanie obliczen w watku"""


 
Przykład

#!/usr/bin/python

import threading
import time

exitFlag = 0

class myThread (threading.Thread):


def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
print_time(self.name, self.counter, 5)
print "Exiting " + self.name

def print_time(threadName, delay, counter):


while counter:
if exitFlag:
thread.exit()
time.sleep(delay)
10 
 
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1

# Create new threads


thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads


thread1.start()
thread2.start()

print "Exiting Main Thread"



Rezultat działania
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
11 
 
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2

   

12 
 

Maraton (przykład)
import threading
Rozpoczęcie biegu:
total_distance = 0

r1 = runner(1)
class runner(threading.Thread):
r2 = runner(2)
def __init__(self, nr_startowy):
r1.start()
self.numer = nr_startowy
r2.start()
threading.Thread.__init__(self)
r1.join()
def run(self):
r2.join()
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’ % self.numer
print ’Zawodnik %i na mecie’ % self.numer
13 
 
Maraton c.d
Rozpoczęcie biegu:

r1 = runner(1)
r2 = runner(2)
r1.start()
r2.start()
r1.join()
r2.join()
print ’koniec wyscigu, dystans’, total_distance

Główny program to też wątek, więc po wywołaniu r1.start() są dwa wątki


r1.join() 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(threading.Thread):
...
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
...
lock.acquire()
total_distance = total_distance + 1
lock.release()


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)
sem.acquire()
sem.acquire()
sem.acquire()
sem.acquire() # 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
lck.acquire()
for i in range(5):
szklanka_mleka = szklanka_mleka + 1
lck.release()

#wypijanie

while szklanka_mleka ! = 5: pass


lck.acquire()
while szklanka_mleka > 0:
szklanka_mleka = szklanka_mleka - 1
lck.release()
19 
 
kolejny przykład
#!/usr/bin/python

import threading
import time

class myThread (threading.Thread):


def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
threadLock.release()

def print_time(threadName, delay, counter):


while counter:
20 
 
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1

threadLock = threading.Lock()
threads = []

# Create new threads


thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads


thread1.start()
thread2.start()

# Add threads to thread list


threads.append(thread1)
threads.append(thread2)

# Wait for all threads to complete


for t in threads:
t.join()
print "Exiting Main Thread"

21 
 
Wynik działania programu
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread

22 
 
Usypianie i budzenie wątkó w
lck = threading.Condition()

# Wypijanie mleka
lck.acquire()
while szklanka_mleka != 5:
lck.wait() #Usypianie
while szklanka_mleka > 0: szklanka_mleka = szklanka_mleka - 1
lck.release()

#Nalewanie mleka
lck.acquire()
for i in range(5):
szklanka_mleka = szklanka_mleka + 1
lck.notify() #budzenie
lck.release()

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 (threading.Thread):


def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print "Starting " + self.name
process_data(self.name, self.q)
print "Exiting " + self.name

def process_data(threadName, q):


while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
26 
 
queueLock.release()
print "%s processing %s" % (threadName, data)
else:
queueLock.release()
time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]


nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1

# Create new threads


for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1

# Fill the queue


queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()

27 
 
# Wait for queue to empty
while not workQueue.empty():
pass

# Notify threads it's time to exit


exitFlag = 1

# Wait for all threads to complete


for t in threads:
t.join()
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 
 

You might also like