Python semaphores, condition variables, events

Posted by elabuwa on Sun, 09 Jan 2022 17:33:50 +0100

Semaphores, in other words, control the number of concurrent threads, which is the number of concurrent threads in turn

See the example:

import threading  # Import module
import time  # Import module


class Exam(threading.Thread):  # Create class
    def __init__(self, num):  # initialization
        super(Exam, self).__init__()  # Parent class initialization
        self.num = num  # initialize variable

    def run(self):  # Override parent method
        semaphore.acquire()  # Semaphore acquisition
        print(self.num)  # Print
        time.sleep(2)  # Delay 2 seconds
        semaphore.release()  # Release semaphore


if __name__ == "__main__":  # Main function
    semaphore = threading.Semaphore(3)  # Create an instance and set the semaphore to 3
    for i in range(50):  # 50 cycles
        exam = Exam(60)  # Create instance
        exam.start()  # Open thread

Output results:

We found that after setting the semaphore, it is a group of every three, that is, after three threads, and then the next three

Condition variable:

Condition variables are usually associated with a Lock. When you need to share a Lock among multiple contidions, you can pass a Lock/RLock instance to the constructor, otherwise it will generate an RLock instance itself. It can be understood that in addition to the Lock pool with Lock, condition also includes a wait pool. The threads in the pool are in the wait blocking state in the state diagram until another thread calls notify() to notify subsequent threads. After receiving the notification, the thread enters the Lock pool to wait for locking, That is, the running of one thread can accommodate another thread to run halfway from the first run

import threading  # Import module
import time  # Import module


class Test(threading.Thread):  # Create class
    def __init__(self, num):  # initialization
        super(Test, self).__init__()  # Parent class initialization
        self.num = num  # initialize variable

    def run(self):  # Override parent method
        con.acquire()  # Acquire lock
        print("Thread:" + self.num)  # Print
        con.notify()  # Notify subsequent threads
        print("thread " + self.num + "Hang")  # Print
        con.wait()  # The thread hangs until it times out or receives a notify notification
        time.sleep(2)  # Delay 2 seconds
        print("thread " + self.num + "Restart")  # Print
        con.notify()  # Notify subsequent threads
        con.release()  # Release lock


class Test2(threading.Thread):
    def __init__(self, num2):
        super(Test2, self).__init__()
        self.num2 = num2

    def run(self):
        con.acquire()  # Acquire lock
        print("Thread:" + self.num2)  # Print
        con.notify()  # Notify subsequent threads
        print("thread " + self.num2 + "Hang")  # Print
        con.wait()  # The thread hangs until it times out or receives a notify notification
        time.sleep(2)  # Delay 2 seconds
        print("thread " + self.num2 + "Restart")  # Print
        con.notify()  # Notify subsequent threads
        con.release()  # Release lock


if __name__ == "__main__":  # Main function
    con = threading.Condition()  # Create a conditional variable instance
    t1 = Test(str(1))  # Create instance
    t1.start()  # Open thread
    t2 = Test2(str(2))  # Create instance
    t2.start()  # Open thread

Output results

It can be seen that after thread 1 is started, thread 2 will be notified to start, then thread 1 will hang, then thread 2 will start, then thread 1 will be notified, then thread 2 will hang, then thread 1 will start again, and then thread 2 will be notified to start again. This is the operation principle of condition variables.

event:

import threading  # Import module
import time  # Import module


class Test(threading.Thread):  # Create class
    def __init__(self):  # initialization
        super(Test, self).__init__()  # Parent class initialization
        # self.num = num  # initialize variable

    def run(self):  # Override parent method
        while True:  # Infinite loop
            if event.is_set():  # Determine whether to set events
                print("Event trigger")  # Print
            else:  # negative
                print("Event stop")  # Print
                event.wait()  # Wait for the event to stop for 3 seconds


class Test2(threading.Thread):  # Create class
    def __init__(self, ):  # initialization
        super(Test2, self).__init__()  # Parent class initialization
        # self.num2 = num2

    def run(self):  # Rewrite classification method
        while True:  # Infinite loop
            event.set()  # Set event
            time.sleep(1)  # Delay 1 second
            event.clear()  # Clear event
            time.sleep(3)  # Delay 3 seconds


if __name__ == "__main__":  # Main function
    event = threading.Event()  # Create time instance
    t1 = Test()  # Create instance
    t1.start()  # Open thread
    t2 = Test2()  # Create instance
    t2.start()  # Open thread

Output results:

In the interval, when the if judges that the setting event is detected, it will print "event trigger" and then delay for one second, but the program will also detect whether the event still exists. When the delay occurs, the event does exist, so it will print all the time. When the event is cleared (clear method), the program detects that the event is cleared, so it will print "event stop" and delay for 3 seconds, Ordered programs are infinite loops, so they continue to recreate events, cycle over and over, and continue the whole repeated process.

 

Topics: Python Back-end