[Python multi-threaded programming - threading module] - Python multi-threaded series of articles (2)


foreword

Previous articleIntroduction to the threading module Portal:

https://blog.csdn.net/youngwyj/article/details/124720041

Thread synchronization is an important concept in multithreading. When multiple threads need to share data, if thread synchronization is not used, data will be out of sync.
There are two methods to achieve thread synchronization, thread lock and condition variable Condition.


1. Thread lock

1.Lock lock

The Lock lock in the threading module is the same as the lock in the _thread module.
Example:

import threading
import time

num = 0
lock = threading.Lock()  #申请线程锁

class threadTest(threading.Thread):  #类必须继承threading.Thread
    
    def __init__(self) -> None:  #args为传入线程的参数,可根据自己的需求进行定义
        
        super(threadTest,self).__init__()  #初始化super()内的必须与类名一样

    def run(self) -> None:  #定义run()方法,主要写线程的执行内容

        global num  #声明全局变量num

        # lock.acquire()  #申请线程锁

        print('子线程' + self.getName() + '开始:' + str(time.time()))
        while num < 5:
            time.sleep(2)  
            print(self.getName(),'num:',num)
            num += 1
        #休眠2s
        print('子线程' + self.getName() + '结束:' + str(time.time()))

        # lock.release()  #释放线程锁
        
        return super().run()

if __name__ == '__main__':

    print('主线程开始:%s'%(str(time.time())))
    thread1 = threadTest()
    thread1.setName('Thread-1') #设置线程名称
    thread2 = threadTest()
    thread2.setName('Thread-2') #设置线程名称    
    thread1.start()  #启动线程
    thread2.start()  #启动线程
    time.sleep(1)
    thread1.join()
    thread2.join()
    print('主线程已结束:%s'%(str(time.time())))

operation result:

主线程开始:1652803641.5234373
子线程Thread-1开始:1652803641.5236645
子线程Thread-2开始:1652803641.5238614
Thread-2 num: 0
Thread-1 num: 0
Thread-2 num: 2
Thread-1 num: 3
Thread-2 num: 4
子线程Thread-2结束:1652803647.5305896
Thread-1 num: 5
子线程Thread-1结束:1652803647.5308123
主线程已结束:1652803647.531019

It can be seen that when the lock thread is not used, thread 1 and thread 2 have confusion about the num operation.
Remove the comments from the above two lines of code lock.acquire() lock.release() and add the thread lock.
The running results are as follows: You can see that there will be no confusion like the above.

主线程开始:1652804037.7551372
子线程Thread-1开始:1652804037.7553797
Thread-1 num: 0
Thread-1 num: 1
Thread-1 num: 2
Thread-1 num: 3
Thread-1 num: 4
子线程Thread-1结束:1652804047.7664511
子线程Thread-2开始:1652804047.766612
子线程Thread-2结束:1652804047.7667737
主线程已结束:1652804047.7669005

2. RLock lock

RLock lock, also known as recursive lock, differs from Lock lock in that Lock lock only allows one application in the same thread, otherwise the thread will enter deadlock, but RLock allows multiple calls in the same thread.
Example code for deadlock using Lock lock:

import threading
import time

print('主线程开始:%s'%(str(time.time())))
lock = threading.Lock()

lock.acquire()    #申请线程锁
print(threading.enumerate())

lock.acquire()    #再次申请线程锁,产生了死锁
print(threading.enumerate())

lock.release()
lock.release()
print('主线程结束:%s'%(str(time.time())))

operation result:

主线程开始:1652804393.5089679
[<_MainThread(MainThread, started 140384603543360)>]
^Z					#此处为我在终端中主动杀死了线程,并非程序自己结束
[1]+  已停止

Using RLock lock will not cause deadlock sample code:

import threading
import time

print('主线程开始:%s'%(str(time.time())))
lock = threading.RLock()

lock.acquire()    #申请线程锁
print(threading.enumerate())

lock.acquire()    #再次申请线程锁,不会产生死锁
print(threading.enumerate())

lock.release()
lock.release()
print('主线程结束:%s'%(str(time.time())))

operation result:

主线程开始:1652804609.3881009
[<_MainThread(MainThread, started 140639696934720)>]
[<_MainThread(MainThread, started 140639696934720)>]
主线程结束:1652804609.3881621

From the above, you can see the difference between Lock and RLock
Note that thread locks need to appear in pairs

Two, the condition variable Condition

Condition is a more advanced lock in python3. In addition to the acquire() and release() functions similar to thread locks, it also provides the following functions.

function illustrate
wait() suspend the thread
notify() Wake up a suspended thread to run
notifyAll() Wake up all threads to run

Note: The lock needs to be obtained before the thread is used, otherwise a RuntimeError exception will be thrown

It can be understood that Condition provides a multi-thread communication mechanism. If thread 1 needs data, thread 1 will block and wait. Thread 2 produces data. After thread 2 produces data and notifies thread 1, thread 1 can go to got the data

The following is a sample code for simulating idiom Solitaire using the condition variable Condition:

import threading
import time

class Man1(threading.Thread):  #类必须继承threading.Thread
    
    def __init__(self,lock) -> None:  #args为传入线程的参数,可根据自己的需求进行定义
        
        super(Man1,self).__init__()  #初始化super()内的必须与类名一样
        self.lock = lock

    def run(self):
        self.lock.acquire()  #申请锁
        print('子线程' + self.getName() + '为所欲为')

        self.lock.wait()   #挂起线程,等待回答
        print('子线程' + self.getName() + '逼上梁山')
        self.lock.notify()  #运行挂起的线程

        self.lock.wait()
        print('子线程' + self.getName() + '尽力而为')
        self.lock.notify()

        self.lock.release()

class Man2(threading.Thread):  #类必须继承threading.Thread
    
    def __init__(self,lock) -> None:  #args为传入线程的参数,可根据自己的需求进行定义
        
        super(Man2,self).__init__()  #初始化super()内的必须与类名一样
        self.lock = lock

    def run(self):
        self.lock.acquire()

        self.lock.notify()   #唤醒对方线程
        print('子线程' + self.getName() + '为法自弊')
        self.lock.wait()   #挂起线程,等待回答
        
        self.lock.notify()  
        print('子线程' + self.getName() + '山穷水尽')
        self.lock.wait()
        
        self.lock.notify()       
        print('子线程' + self.getName() + '为所欲为')

        self.lock.release()

if __name__ == '__main__':
    lock = threading.Condition()
    man1 = Man1(lock)
    man2 = Man2(lock)
    man1.setName('Thread-1') #设置线程名称
    man2.setName('Thread-2') #设置线程名称   
    print('成语接龙开始:')
    man1.start()
    man2.start()

operation result:

成语接龙开始:
子线程Thread-1为所欲为
子线程Thread-2为法自弊
子线程Thread-1逼上梁山
子线程Thread-2山穷水尽
子线程Thread-1尽力而为
子线程Thread-2为所欲为

It can be seen that under the control of the condition variable, the two threads execute in sequence until the end

Guess you like

Origin blog.csdn.net/youngwyj/article/details/124833126