Welcome to another tutorial, here you will learn about Thread Synchronization on object conditioning in Python.
During programming to synchronize to gain access to any resources efficiently, thus you can associate a condition with tasks for any thread to wait until a certain condition is met, or to notify other threads about the condition being fulfilled, in order for them to unblock themselves.
Have a look at this illustration base on a Producer-Consumer issue. Suppose a certain producer produces a few items and a certain consumer consumes them, as such, it is until the Producer has produced the item that the Consumer can consume it. Therefore, the consumer has to wait till the producer produces an item; on the other hand, it is the duty of the Producer to inform the Consumer that an item is available for consumption once it is produced successfully.
And if multiple Consumers are consuming the item produced by the Producer then the Producer must inform all the Consumers about the new item produced.
In Python. the illustration above can be seen as perfect for the condition object in multithreading.
Below is the general syntax for the condition object.
condition = threading.Condition([lock])
The condition object needs an optional lock object as an input argument.
In addition, the condition object has the acquire() and release() methods that call the corresponding methods of the associated lock. Also, it has the methods wait(), notify() and notifyAll(). However, three methods must be called only after the calling thread has acquired the lock.
The condition class methods are highlighted below:
This method is typically used to acquire the lock. It calls the corresponding to acquire() method on the underlying lock present in the condition object, and the return value is whatever that method returns.
The release() method is primarily used to release the lock. It calls the corresponding release() method on the underlying lock exiting in the condition object.
In this case, The method is used to block the thread and make it wait until another thread notifies it by calling the notify() or notifyAll() method on the same condition object or until the timeout occurs. In addition, this method must be called only when the calling thread has acquired the lock.
However, when it is called, it releases the lock and then blocks the thread until it is awakened by a notify() or notifyAll() call for the same condition variable from some other thread, or whenever the timeout happens.
This method is used to wake up any thread waiting on the corresponding condition. It must be called only when the calling thread has acquired the lock. But, calling this method will wake one waiting thread only.
This type of method wakes up all the threads waiting on the given condition. It typically acts like notify() method, but wakes up all the waiting threads rather than one.
From this example, we implemented a simple producer-consumer solution, in which the producer produces an item and adds it to a list from which the consumer is consuming the items.
import threading
import time
from random import randint
class SomeItem:
# init method
def __init__(self):
# initialize empty list
self.list = []
# add to list method for producer
def produce(self, item):
print("Adding item to list...")
self.list.append(item)
# remove item from list method for consumer
def consume(self):
print("consuming item from list...")
item = self.list[0]
print("Item consumed: ", item)
self.list.remove(item)
def producer(si, cond):
r = randint(1,5)
# creating random number of items
for i in range(1, r):
print("working on item creation, it will take: " + str(i) + " seconds")
time.sleep(i)
print("acquiring lock...")
cond.acquire()
try:
si.produce(i)
cond.notify()
finally:
cond.release()
def consumer(si, cond):
cond.acquire()
while True:
try:
si.consume()
except:
print("No item to consume...")
# wait with a maximum timeout of 10 sec
val = cond.wait(10)
if val:
print("notification received about item production...")
continue
else:
print("waiting timeout...")
break
cond.release()
if __name__=='__main__':
# condition object
cond = threading.Condition()
# SomeItem object
si = SomeItem()
# producer thread
p = threading.Thread(target=producer, args=(si,cond,))
p.start()
# consumer thread
c = threading.Thread(target=consumer, args=(si,cond,))
c.start()
#print('Waiting for producer and consumer threads...')
p.join()
c.join()
print("Done")
Output:
working on item creation, it will take: 1 secondsconsuming item from list...
No item to consume...
acquiring lock...
Adding item to list...
working on item creation, it will take: 2 seconds
notification received about item production...
consuming item from list...
Item consumed: 1
consuming item from list...
No item to consume...
acquiring lock...
Adding item to list...
working on item creation, it will take: 3 seconds
notification received about item production...
consuming item from list...
Item consumed: 2
consuming item from list...
No item to consume...
acquiring lock...
Adding item to list...
notification received about item production...
consuming item from list...
Item consumed: 3
consuming item from list...
No item to consume...
waiting timeout...
Done
Note the following important things.