Un exemplu de program in Python care afiseaza (oarecum aleator) id-urile unor thread-uri aflate in executie:
import time
from threading import Thread
from threading import Lock
class MyThread(Thread):
def __init__(self,n):
Thread.__init__(self)
self.tid=n
def run(self):
a = 0
for i in range(100000):
a += 1
print self.tid
def test():
nt = 10
for i in range(nt):
thr1=MyThread(i)
thr1.start()
thr1.join()
test()
Elementele de sincronizare in Python sunt:
* Lock - se poate afla intr-una din doua stari:
- blocat: daca se apeleaza release() se trece in starea neblocat
- neblocat (este creat neblocat): daca se apeleaza acquire() se trece in starea blocat
Exemplu: Acelasi program de mai sus, insa cu id-urile threadurilor afisate ordonat (se foloseste Lock pentru ca in momentul afisarii zona sa fie exclusiv a firului de executie si sa nu poata interfera un alt thread)
import time
from threading import Thread
from threading import Lock
# afisare in ordine folosind lock
class MyThread(Thread):
def __init__(self,n):
Thread.__init__(self)
self.tid=n
def run(self):
a = 0
lock.acquire()
for i in range(100000):
a += 1
print self.tid
lock.release()
def test():
nt = 10
for i in range(nt):
thr1=MyThread(i)
thr1.start()
thr1.join()
lock = Lock()
test()
* Mecanisme de comunicatie - evenimente si conditii
Evenimente: un thread semnalizeaza un eveniment - notify(), iar altul asteapta ca evenimentul sa se intample - wait().
In Python, un obiect de tip event are un flag intern, initial setat pe false. Acesta poate fi setat pe true cu functia set() si resetat folosind clear(). Pentru a verifica starea flag-ului, se apeleaza functia isSet().
Un alt thread poate folosi metoda wait([timeout]) pentru a astepta ca un eveniment sa se intample (ca flag-ul sa devina true): daca in momentul apelarii wait(), flag-ul este true, thread-ul apelant nu se blocheaza, dar daca este false se blocheaza pana la setarea eventului. De altfel, la un set(), toate thread-urile care asteptau event-ul cu wait() vor fi notificate (si eligibile deci pentru a rula).
O variabila de tip conditie (Condition) este asociata cu un obiect mutex Lock. Acesta poate fi transmis ca parametru atunci cand mai multe variabile condition partajeaza un lock sau poate fi creat implicit.
Metodele prezente sunt acquire() si release() care le apeleaza pe cele corespunzatoare lock-ului si mai exista functiile wait(), notify() si notifyAll(), apelabile doar daca s-a reusit obtinerea lock-ului:
- metoda wait() elibereaza lock-ul si se blocheaza in asteptarea unei notificari
- metoda notify() deblocheaza un singur thread aflat in asteptare
- metoda notifyAll() deblocheaza toate thread-urile care asteptau indeplinirea conditiei
! Apelurile notify() si notifyAll() nu elibereaza lock-ul, deci un thread nu va fi trezit imediat ci doar cand apeluri de mai sus au terminat de folosit lock-ul si l-au eliberat.
* Semafoarele - sunt obiecte de sincronizare similare Lock-urilor insa difera de acestea prin faptul ca salveaza numarul de operatii de deblocare efectuate asupra lor. Un semafor gestioneaza un contor intern care este decrementat de un apel acquire() si incrementat de apelul release().
Contorul nu poate ajunge la valori negative deci atunci cand este apelata functia acquire() si contorul este 0 thredul se blocheaza pana cand alt thread apeleaza release(). Atunci cand este creat un semafor contorul are valoarea 1.
Exemplu: Problema producator-consumator, cand 3 producatori, 1 consumator si buffer nelimitat.
import time
import random
from threading import Thread
import threading
sem1 = threading.Semaphore(value=3) # 3 prod
# cons nu are semafor
class Prod(Thread):
def __init__(self,buf,n):
Thread.__init__(self)
self.idt = n
self.buf = buf
def run(self):
while True:
global sem1
sem1.acquire()
self.buf.append(1)
print "Prod",self.idt, " produce; Buffer=", self.buf
time.sleep(random.random()*5)
sem1.release()
class Cons(Thread):
def __init__(self,buf,idt):
Thread.__init__(self)
self.idt = idt
self.buf = buf
def run(self):
while True:
if len(self.buf) > 0:
del self.buf[0]
print "Cons",self.idt, " consuma; Buffer=", self.buf
time.sleep(random.random()*5)
def test():
buf = []
p1 = Prod(buf,0)
p2 = Prod(buf,1)
p3 = Prod(buf,2)
c = Cons(buf,0)
p1.start()
p2.start()
p3.start()
c.start()
test()