Curs 11

Download as pdf or txt
Download as pdf or txt
You are on page 1of 35

PROGRAMMING IN PYTHON Gavrilut Dragos

Course 11
THREADING AND SYNCHRONIZATION
Python threading support is achieved through two modules:
o _thread ➔ old methods, low-level methods
o threading ➔ new methods, based on a class model

Details about these modules can be found on:


❖ Python 3: https://docs.python.org/3/library/_thread.html
❖ Python 3: https://docs.python.org/3/library/threading.html

Besides these a series of synchronization object that include locks, semaphores, events
are also available.
As thread module was renamed in python 3 to _thread. It is best to use threading if you
want a code that will run in the same way in Python 2 and Python 3.
THREADING AND SYNCHRONIZATION
To start a new thread use start_new_thread method from class thread. The method
receives a function that will be executed on the new thread and functions parameters.
Python 3.x
import _thread,time
Output
def MyPrint(sleepPeriod,name,count): Thread #1=>0
for i in range(0,count): Thread #3=>0
Thread #2=>0
print (name+"=>"+str(i)) Thread #1=>1
time.sleep(sleepPeriod) Thread #2=>1
Thread #1=>2
#main thread
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
_thread.start_new_thread (MyPrint, (2,"Thread #2", 2))
_thread.start_new_thread (MyPrint, (3,"Thread #3", 1))
time.sleep(10)
THREADING AND SYNCHRONIZATION
In case of objects that are not thread-safe a lock can be used. Output 1 Output 2
Python 3.x Thread #1=>0 Thread #3=>0
Thread #3=>0 Thread #1=>0
import _thread,time Thread #2=>0 Thread #2=>0
lock = _thread.allocate_lock() Thread #1=>1 Thread #1=>1
def MyPrint(sleepPeriod,name,count): Thread #1=>2 Thread #2=>1
Thread #2=>1 Thread #1=>2
global lock Thread #3=>1 Thread #3=>1
for i in range(0,count): Thread #2=>2 Thread #2=>2
lock.acquire() Thread #3=>2 Thread #3=>2
print (name+"=>"+str(i)) Thread #3=>3 Thread #3=>3

lock.release()
time.sleep(sleepPeriod)
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
_thread.start_new_thread (MyPrint, (2,"Thread #2", 3))
_thread.start_new_thread (MyPrint, (3,"Thread #3", 4))
time.sleep(10)
THREADING AND SYNCHRONIZATION
Locks can also be used with with statement (in this case the acquire and release are
called in __enter __ and __exit__ code ) Output 1 Output 2
Python 3.x Thread #1=>0 Thread #2=>0
import _thread,time Thread #3=>0 Thread #1=>0
lock = _thread.allocate_lock() Thread #2=>0 Thread #3=>0
Thread #1=>1 Thread #1=>1
def MyPrint(sleepPeriod,name,count): Thread #1=>2 Thread #2=>1
global lock Thread #2=>1 Thread #1=>2
for i in range(0,count): Thread #3=>1 Thread #3=>1
Thread #2=>2 Thread #2=>2
with lock: Thread #3=>2 Thread #3=>2
print (name+"=>"+str(i)) Thread #3=>3 Thread #3=>3
time.sleep(sleepPeriod)
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
_thread.start_new_thread (MyPrint, (2,"Thread #2", 3))
_thread.start_new_thread (MyPrint, (3,"Thread #3", 4))
time.sleep(10)
THREADING AND SYNCHRONIZATION
Locks can also be used to wait for a thread to finish.
Python 3.x
import _thread,time
lock = _thread.allocate_lock()
lock.acquire() Output
def MyPrint(sleepPeriod,name,count): Waiting for a thread to finish ...
Thread #1=>0
global lock Thread #1=>1
for i in range(0,count): Thread #1=>2
print (name+"=>"+str(i)) Thread finished
time.sleep(sleepPeriod)
lock.release()
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
print ("Waiting for a thread to finish ...")
lock.acquire()
print ("Thread finished")
THREADING AND SYNCHRONIZATION
Locks can also be used to wait for a thread to finish.
Python 3.x
import _thread,time
lock = _thread.allocate_lock() Step 1:
lock.acquire() lock variable is acquired
def MyPrint(sleepPeriod,name,count): before any thread is started.
global lock
for i in range(0,count):
print (name+"=>"+str(i))
time.sleep(sleepPeriod)
lock.release()
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
print ("Waiting for a thread to finish ...")
lock.acquire()
print ("Thread finished")
THREADING AND SYNCHRONIZATION
Locks can also be used to wait for a thread to finish.
Python 3.x
import _thread,time Step 2:
lock = _thread.allocate_lock() Main thread tries to acquire
lock.acquire() again the lock variable. As
def MyPrint(sleepPeriod,name,count): this variable was already
global lock acquired, the main thread
for i in range(0,count): will wait until lock variable is
print (name+"=>"+str(i)) released.
time.sleep(sleepPeriod)
lock.release()
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
print ("Waiting for a thread to finish ...")
lock.acquire()
print ("Thread finished")
THREADING AND SYNCHRONIZATION
Locks can also be used to wait for a thread to finish.
Python 3.x
import _thread,time Step 3:
lock = _thread.allocate_lock() When “Thread #1” is
lock.acquire() finished the lock variable is
def MyPrint(sleepPeriod,name,count): released. At that point the
global lock call to lock.acquire from the
for i in range(0,count): main thread will be executed
print (name+"=>"+str(i)) and the script will continue.
time.sleep(sleepPeriod)
lock.release()
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
print ("Waiting for a thread to finish ...")
lock.acquire()
print ("Thread finished")
THREADING AND SYNCHRONIZATION
Exceptions not caught in a different thread than the main thread will not stop the
program.
Python 3.x
import _thread,time
def MyPrint(sleepPeriod,name,count):
global lock
for i in range(-count,count):
print (name+"=>"+str(10/i))
time.sleep(sleepPeriod)
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
for i in range(0,10):
print ("Main thread : "+str(i))
time.sleep(1)
THREADING AND SYNCHRONIZATION
Exceptions not caught in a different thread than the main thread will not stop the
program.
Output
Python 3.x Main thread : 0
import _thread,time
Thread #1=>-3.3333333333333335
Thread #1=>-5.0
def MyPrint(sleepPeriod,name,count):
Main thread : 1
global lock
Thread #1=>-10.0
for Main
i inthread
range(-count,count):
: 2
Unhandled
printexception in thread started by <function MyPrint at 0x019C3810>
(name+"=>"+str(10/i))
Traceback (most recent call last):
time.sleep(sleepPeriod)
Main thread : 3
_thread.start_new_thread (MyPrint, (1,"Thread #1", 3))
File "E:\Documente\Facultate\Python\2019-2020\p.py", line 5, in MyPrint
for i in range(0,10):
print (name+"=>"+str(10/i))
ZeroDivisionError:
print ("Main threaddivision by zero
: "+str(i))
Main thread : 4
time.sleep(1)
Main thread : 5
Main thread : 6
Main thread : 7
Main thread : 8
Main thread : 9
THREADING AND SYNCHRONIZATION
Threading module provides high level functions for thread workers and synchronization.
It also provides a class Thread that can be used to derive thread based objects. When
deriving from a Thread class two methods are usually implemented:
o run() ➔ code that will be executed when the thread starts
o __init__ ➔ thread constructor (it is important to call __init__ from thee base class before doing
anything with the thread

Thread class has the following methods:


o start() ➔ starts the thread
o join(timeout) ➔ waits for the thread to finish
o getName/setName and name attribute ➔ indicate the name of the thread (if needed)
o is_alive() ➔ return true if the thread is alive
THREADING AND SYNCHRONIZATION
Using threading.Thread without sub-classing
Python 3.x
import threading,time
Output
Wait for the thread to complete ...

def WaitSomeSeconds(seconds):
time.sleep(seconds)

t = threading.Thread(target=WaitSomeSeconds, args = (5,))


t.start()
print("Wait for the thread to complete ...") The target function expect a
t.join() touple with arguments. If that
touple contains only one
parameter, a ‘,’ must be
added to specify a touple.
THREADING AND SYNCHRONIZATION
Using threading.Thread without sub-classing
Python 3.x
import threading,time
Output
Wait for the thread to complete ...
30
def WaitSomeSeconds(seconds,x,y):
time.sleep(seconds)
print(x+y)

t = threading.Thread(target=WaitSomeSeconds, args = (5,10,20))


t.start()
print("Wait for the thread to complete ...")
t.join()
THREADING AND SYNCHRONIZATION
Sub-classing threading.Thread. Thread code will be added in “run” method.
Python 3.x
import threading,time
Output
Wait for the thread to complete ...
class Mythread(threading.Thread):
def __init__(self,seconds):
threading.Thread.__init__(self)
self.seconds = seconds
def run(self):
time.sleep(self.seconds)

t = Mythread(3)
t.start()
print("Wait for the thread to complete ...")
t.join()
SYNCHRONIZATION
The following synchronization object are available in threading module:
o lock
o rlock (reentrant lock)
o Condition objects
o Semaphore
o Event
o Timer
o Barrier
SYNCHRONIZATION (LOCK)
Allows synchronized access to a resource.
Lock objects have two functions:
1. Python 3: Lock.acquire(blocking=True, timeout=-1) (timeout means how many seconds
the Lock has to wait until it is acquired.

2. Lock.release() ➔ releases the lock. If called on an unlocked lock, an error will be


raised.
Lock objects also support working with with keyword.
SYNCHRONIZATION (LOCK)
Using Lock object (there is no guarantee that the number will be in order !!!)
Python 3.x
import threading,time
l = threading.Lock() Output
def ThreadFnc(lock,n_list,start):
for i in range(0,10): [100, 1000, 1001, 101, 1002, 102, 1003, 103,
lock.acquire() 1004, 104, 1005, 105, 106, 1006, 107, 1007,
108, 1008, 109, 1009]
n_list+=[start+i]
lock.release()
time.sleep(1)
lst = []
t1 = threading.Thread(target=ThreadFnc, args=(l,lst,100))
t2 = threading.Thread(target=ThreadFnc, args=(l,lst,1000))
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (LOCK)
Using Lock object with with keyword
Python 3.x
import threading,time
l = threading.Lock()
def ThreadFnc(lock,n_list,start):
for i in range(0,10):
with lock: n_list+=[start+i]
time.sleep(1)
lst = []
t1 = threading.Thread(target=ThreadFnc, args=(l,lst,100))
t2 = threading.Thread(target=ThreadFnc, args=(l,lst,1000))
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (RLOCK)
Allows reentrant lock (the same thread can lock a resources multiple times).
RLock objects have two functions:
1. Python 3: Lock.acquire(blocking=True, timeout=-1) (timeout means how many seconds
the Lock has to wait until it is acquired.
Lock.acquire returns true if the lock was acquired, false otherwise. If the lock was
already acquire by the same thread, a counter is increased and true is returned.
2. Lock.release() ➔ decreases the counter. Once it reaches 0, the lock is unlocked.
RLock objects also support working with with keyword.
Within the same thread, be sure that the number of acquire queries is thee same as the
number of release (otherwise you risk keeping the lock unlocked !!!)
SYNCHRONIZATION (RLOCK)
Python 3.x
import threading
Current program will never end.
l = threading.Lock()
def ThreadFnc1(lock):
When ThreadFnc2 calls ThreadFnc1,
with lock: print("fnc_1_called") the lock is already block and a
def ThreadFnc2(lock): dead-lock is produced.
with lock:
print("fnc_2_called")
ThreadFnc1(lock)
t1 = threading.Thread(target=ThreadFnc1, args=(l,))
t2 = threading.Thread(target=ThreadFnc2, args=(l,))
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (RLOCK)
Python 3.x
import threading
l = threading.RLock() If we replace Lock with RLock
def ThreadFnc1(lock): the same code will function as
with lock: print("fnc_1_called")
def ThreadFnc2(lock):
it should.
with lock:
print("fnc_2_called")
ThreadFnc1(lock)
t1 = threading.Thread(target=ThreadFnc1, args=(l,))
t2 = threading.Thread(target=ThreadFnc2, args=(l,))
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (CONDITION OBJECT)
Provides a notification system to other systems based on a condition. It has the following
methods:
o acquire
o release
o wait
o wait_for (Python 3)
o notify
o notify_all

Conditional objects also support working with with keyword.


SYNCHRONIZATION (CONDITION OBJECT)
Python 3.x
import threading,time
c = threading.Condition()
number = 0
def ThreadConsumer(): Output (after 2 seconds)
global number,c
with c: Consume: 5
if number==0: c.wait()
print("Consume: "+str(number))
number = 0
def ThreadProducer():
global number,c
with c:
time.sleep(2)
number = 5
c.notify()
t1 = threading.Thread(target=ThreadConsumer)
t2 = threading.Thread(target=ThreadProducer)
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (CONDITION OBJECT)
Python 3.x
import threading,time
c = threading.Condition()
number = 0
def ThreadConsumer(): Output (after 2 seconds)
global number,c
with c: Consume: 5
c.wait_for(lambda: number!=0)
print("Consume: "+str(number))
number = 0
def ThreadProducer():
global number,c
with c:
time.sleep(2)
number = 5
c.notify()
t1 = threading.Thread(target=ThreadConsumer)
t2 = threading.Thread(target=ThreadProducer)
t1.start ()
t2.start ()
t1.join ()
t2.join ()
SYNCHRONIZATION (SEMAPHORES)
Provides access to a limited number of threads to a resource. It has the following
functions:
o acquire
o release

Conditional objects also support working with with keyword.


SYNCHRONIZATION (SEMAPHORES) Output
Python 3.x Thread-#0 enter
Thread-#1 enter
import threading,time Thread-#2 enter
s = threading.Semaphore(4) Thread-#3 enter
def WorkerThread(id): Thread-#3 exit
global s Thread-#2 exit
Thread-#0 exit
with s: Thread-#4 enter
print("Thread-#"+str(id)+" enter") Thread-#1 exit
time.sleep(1) Thread-#5 enter
Thread-#6 enter
print("Thread-#"+str(id)+" exit") Thread-#7 enter
t = [] Thread-#6 exit
for i in range(0,10): Thread-#5 exit
t += [threading.Thread(target=WorkerThread, args=(i,))] Thread-#4 exit
Thread-#8 enter
for _th in t: _th.start () Thread-#9 enter
for _th in t: _th.join () Thread-#7 exit
Thread-#8 exit
Thread-#9 exit
SYNCHRONIZATION (TIMER)
Timer is an object deriver from Thread. It allows to run a code after a specific period of
time. A timer also have a cancel method to stop the timer.
Python 3.x
import threading,time
Output
def TimerFunction(mesaj): test after 5 seconds
Done
print (mesaj)

timer = threading.Timer(5,TimerFunction,("test after 5 seconds",))


timer.start()
timer.join()
print("Done")
SYNCHRONIZATION (EVENT)
Event object provides a way to synchronize execution between two or more threads.
It has the following functions:
o set ➔ to signal the current state of the event
o clear ➔ to clear the current state of the event
o wait ➔ wait until the event is signaled ( a call to set method was made)
o is_set ➔ to check if an event was signaled

Events can not be used with with keyword.


To synchronize two thread, two Events are usually used.
SYNCHRONIZATION (EVENT)
Python 3.x
import threading
e1 = threading.Event() Output
e2 = threading.Event() [0,1,2,3,4,5,6,7,8,9]
e1.set()
def AddNumber(start,event1,event2,lista):
for i in range(start,10,2):
event1.wait()
event1.clear()
lista += [i]
event2.set()
l = []
t1 = threading.Thread(target=AddNumber, args=(1,e1,e2,l))
t2 = threading.Thread(target=AddNumber, args=(2,e2,e1,l))
t1.start()
t2.start()
t1.join()
t2.join()
print (l)
SYNCHRONIZATION (BARRIER)
Provides a mechanism to wait for multiple threads to start at the same time.
It has the following functions:
o wait ➔ wait until the number if threads that need to pass a barrier is completed. Only then all threads
are released and will continue their execution
o Reset ➔ resets the barrier
o abort ➔ aborts current barrier
o parties ➔ number of parties (threads) that has to pass the barrier

Barriers can not be used with with keyword.


Barriers are available only on Python 3.
SYNCHRONIZATION (BARRIER)
Python 3.x
import threading,time
b = threading.Barrier(2)

def WorkerThread(b,id):
b_id = b.wait()
print("#"+str(id)+" pass the barier => "+str(b_id))
time.sleep(2)
print("#"+str(id)+" exit")

t = []
for i in range(0,10):
t += [threading.Thread(target=WorkerThread, args=(i,))]
for _th in t: _th.start ()
for _th in t: _th.join ()
SYNCHRONIZATION (BARRIER) Output
#1 pass the barier => 1
Python 3.x #0 pass the barier => 0
#3 pass the barier => 1
import threading,time #2 pass the barier => 0
b = threading.Barrier(2) #5 pass the barier => 1
#4 pass the barier => 0
#7 pass the barier => 1
def WorkerThread(b,id): #6 pass the barier => 0
b_id = b.wait() #9 pass the barier => 1
print("#"+str(id)+" pass the barier => "+str(b_id))
#8 pass the barier => 0
time.sleep(2) #1 exit
#3 exit
print("#"+str(id)+" exit") #2 exit
#0 exit
t = [] #6 exit
#4 exit
for i in range(0,10): #9 exit
t += [threading.Thread(target=WorkerThread, args=(i,))]
#5 exit
for _th in t: _th.start () #7 exit
for _th in t: _th.join () #8 exit
SYNCHRONIZATION (BARRIER) Output
#1 pass the barier => 1
Python 3.x #0 pass the barier => 0
#3 pass the barier => 1
import threading,time #2 pass the barier => 0
b = threading.Barrier(2) #5 pass the barier => 1
Each barrier waits for 2 #4 pass the barier => 0
thread. The b_id #7 pass the barier => 1
def WorkerThread(b,id):
parameter indicates the #6 pass the barier => 0
b_id = b.wait() #9 pass the barier => 1
id of a thread inside a
print("#"+str(id)+" pass the barier => "+str(b_id))#8 pass the barier => 0
barrier. The call to wait #1 exit
time.sleep(2)
exits only when all #3 exit
print("#"+str(id)+" exit")
threads that need to #2 exit
#0 exit
pass the barrier are
t = [] #6 exit
present (in this case #4 exit
for i in range(0,10):
from 2 to 2 threads). #9 exit
t += [threading.Thread(target=WorkerThread, args=(i,))]
#5 exit
for _th in t: _th.start () #7 exit
for _th in t: _th.join () #8 exit
SYNCHRONIZATION (BARRIER)
Python 3.x
import threading,time
b = threading.Barrier(3)

def WorkerThread(b,id): Threads will be group in


b_id = b.wait() groups of 3. As there
print("#"+str(id)+" pass the barier => "+str(b_id))
are 10 threads, thread
time.sleep(2) no. 10 will never end
print("#"+str(id)+" exit") (b.wait will wait until
two more threads will
t = [] enter in the barrier).
for i in range(0,10):
t += [threading.Thread(target=WorkerThread, args=(i,))]
for _th in t: _th.start ()
for _th in t: _th.join ()

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy