Python multithreaded programming (vii) : complex synchronization using Condition

  • 2020-05-05 11:25:26
  • OfStack

So far we've used Lock for mutual exclusive access to common resources, and we've explored the possibility of the same thread using RLock to reenter locks, but despite this, we've only just handled some simple synchronization in our programs, and we can't even reasonably solve the deadlock problem caused by using Lock locks. So we have to learn to solve synchronization problems at a deeper level.

The Condition objects provided by Python provide support for complex thread synchronization problems. Condition is called a condition variable and provides wait and notify methods in addition to acquire and release methods similar to Lock.

The main way to use Condition is for threads to first acquire a condition variable and then determine some conditions. If the condition is not met, wait; If the condition is satisfied, after some processing has been done to change the condition, the other threads are notified via the notify method, and the other threads in the wait state are notified to re-evaluate the condition. Repeat this process over and over again to solve complex synchronization problems.

Here we demonstrate the use of Condition in Python for complex synchronization using the well-known producer-consumer model.


'''
Created on 2012-9-8
 
@author: walfred
@module: thread.TreadTest7
''' 
 
import threading 
import time 
 
condition = threading.Condition() 
products = 0 
 
class Producer(threading.Thread): 
    def __init__(self): 
        threading.Thread.__init__(self) 
 
    def run(self): 
        global condition, products 
        while True: 
            if condition.acquire(): 
                if products < 10: 
                    products += 1; 
                    print "Producer(%s):deliver one, now products:%s" %(self.name, products) 
                    condition.notify() 
                else: 
                    print "Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products) 
                    condition.wait(); 
                condition.release() 
                time.sleep(2) 
 
class Consumer(threading.Thread): 
    def __init__(self): 
        threading.Thread.__init__(self) 
 
    def run(self): 
        global condition, products 
        while True: 
            if condition.acquire(): 
                if products > 1: 
                    products -= 1 
                    print "Consumer(%s):consume one, now products:%s" %(self.name, products) 
                    condition.notify() 
                else: 
                    print "Consumer(%s):only 1, stop consume, products:%s" %(self.name, products) 
                    condition.wait(); 
                condition.release() 
                time.sleep(2) 
 
if __name__ == "__main__": 
    for p in range(0, 2): 
        p = Producer() 
        p.start() 
 
    for c in range(0, 10): 
        c = Consumer() 
        c.start()

In the code, the producer and consumer threads are mainly implemented, and the synchronization problem will be generated around products. First, two generators will produce products, and the next 10 consumers will consume products. The code runs as follows:


Producer(Thread-1):deliver one, now products:1
Producer(Thread-2):deliver one, now products:2
Consumer(Thread-3):consume one, now products:1
Consumer(Thread-4):only 1, stop consume, products:1
Consumer(Thread-5):only 1, stop consume, products:1
Consumer(Thread-6):only 1, stop consume, products:1
Consumer(Thread-7):only 1, stop consume, products:1
Consumer(Thread-8):only 1, stop consume, products:1
Consumer(Thread-10):only 1, stop consume, products:1
Consumer(Thread-9):only 1, stop consume, products:1
Consumer(Thread-12):only 1, stop consume, products:1
Consumer(Thread-11):only 1, stop consume, products:1

In addition, the constructor of Condition object can accept an Lock/RLock object as a parameter. If not specified, the Condition object will create an RLock internally. In addition to the notify method, the Condition object provides an notifyAll method that notifies all threads in the waiting pool to try an acquire internal lock. Because of the above mechanism, threads in the waiting state can only be awakened by the notify method, so the purpose of notifyAll is to prevent threads from being permanently silent.


Related articles: