Python Study --- Thread lock / Semaphore / Conditional variable synchronization
Problem phenomenon : multithreading ,CPU When blocking occurs, the thread will be switched , So it leads to execution tmp-=1 The value of has not been assigned to num=tmp, Another thread 2 It started again tmp -=1, So the last value is assigned to num, So it's here final num Not 0 The situation of .[time.sleep(0.000.) The shorter the break , The smaller the final value ]
import time
import threading
def addNum():
global num # Get this global variable in each thread
temp=num
print('--get num:',num )
time.sleep(0.00000001)
temp -= 1
num = temp
num = 100 # Set a shared variable
thread_list = []
lock=threading.Lock()
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)
for t in thread_list: # Wait for all threads to finish executing
t.join()
print('final num:', num )
Problem solving : Synchronization lock
Classification of locks :
Synchronization lock : Make sure that one thread is using the shared resource at every moment , Avoid the generation of dirty data
die lock : Threads wait for each other to be released
Recursive lock : It consists of an internal timer and a lock , Can be reused , Every use count+1, Release once -1
Synchronization lock :
lock.acquire() # Gets the lock
lock.release() # Release the lock
import threading
def addNum():
global num # Get this global variable in each thread
# num-=1
lock.acquire() # Gets the lock
temp=num
print('--get num:',num )
#time.sleep(0.1)
num =temp-1 # For this common variable -1 operation
lock.release() # Release the lock
num = 100 # Set a shared variable
thread_list = []
lock=threading.Lock()
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)
for t in thread_list: # Wait for all threads to finish executing
t.join()
print('final num:', num )
[ Thread analysis --- Picture from the Internet ]
Deadlock : AB At the same time, it is locked , Wait for the other party to release the lock
terms of settlement : Use recursive locks
import time
import threading
class MyThread(threading.Thread):
def doA(self):
lockA.acquire()
print(self.name, "gotlockA", time.ctime())
time.sleep(3)
lockB.acquire()
print(self.name, "gotlockB", time.ctime())
lockB.release()
lockA.release()
def doB(self):
lockB.acquire()
print(self.name,"gotlockB",time.ctime())
time.sleep(2)
lockA.acquire()
print(self.name,"gotlockA",time.ctime())
lockA.release()
lockB.release()
def run(self):
self.doA()
self.doB()
if __name__ == '__main__':
lockA = threading.Lock()
lockB = threading.Lock()
threads = []
for i in range(5):
threads.append(MyThread())
for i in threads:
i.start()
for i in threads:
i.join()
Recursive lock : Reusable locks [ timer + lock ]
import time
import threading
class MyThread(threading.Thread):
def doA(self):
lock.acquire() # Perform an operation , Lock the thread with a lock
print(self.name, "gotlockA", time.ctime())
time.sleep(3)
lock.acquire() # Perform an operation , Lock the thread with a lock
print(self.name, "gotlockB", time.ctime())
lock.release()
lock.release()
def doB(self):
lock.acquire()
print(self.name,"gotlockB",time.ctime())
time.sleep(2)
lock.acquire()
print(self.name,"gotlockA",time.ctime())
lock.release()
lock.release()
def run(self):
self.doA()
self.doB()
if __name__ == '__main__':
lock = threading.RLock()
threads = []
for i in range(5):
threads.append(MyThread())
for i in threads:
i.start()
for i in threads:
i.join()
ask : Why? RLock There's another one in it Rlock?
answer :1. Prevent other functions from calling data operated functions , cause 2 Data is being written , Generating dirty data
2. It reduces repeated lock operations in other functions to avoid dirty data
import time
import threading
class Account:
def __init__(self, money, id):
self.account = id
self.balance = money
self.r = threading.RLock() # It should be that everyone has his own lock
def add(self, num): # 2 You can add a lock on the method to solve the problem of dirty data caused by other function calls
self.r.acquire()
self.balance += num
self.r.release()
def adwithdrd(self, num):
self.r.acquire()
self.balance -= num
self.r.release()
# def diy(self, num): # 3 It is also a repeated call in a class , That's why there are Rlock Repeated calls to
# self.r.acquire()
# self.balance -= num
# self.adwithdrd(num)
# self.r.release()
def __str__(self):
print(self.balance, self.account)
a1 = Account(500, 'A')
b1 = Account(300, 'B')
# def user_trans(A, B, num):
# A.adwithdrd(num)
# B.add(num) # 1 If a thread operates this function , Will perform add Method , Will affect the final data , The best solution is to add a lock to the class
def trans(A, B, num):
r = threading.RLock()
r.acquire()
A.adwithdrd(num)
B.add(num)
r.release()
t1 = threading.Thread(target=trans, args=(a1, b1, 100))
t3 = threading.Thread(target=trans, args=(a1, b1, 100))
t2 = threading.Thread(target=trans, args=(b1, a1, 200))
t1.start()
t2.start()
t3.start()
a1.__str__()
b1.__str__()
A semaphore used to control the number of concurrent threads ,BoundedSemaphore or Semaphore Manage a built-in count device , Whenever you call acquire() when -1, call release() when +1.
Counter must not be less than 0, When the counter is 0 when ,acquire() Puts the blocked thread into a synchronously locked state , Until another thread calls it release().( Similar to the concept of parking Spaces )
BoundedSemaphore And Semaphore The only difference is that the former will be invoked release() Time check count Whether the value of the counter exceeds the initial value , An exception is thrown if it exceeds
application : Database connection pool
import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(5)
semaphore.release()
if __name__=="__main__":
semaphore=threading.Semaphore(5)
thrs=[]
for i in range(100):
thrs.append(myThread())
for t in thrs:
t.start()
Thread pool
There is a class of threads that need to meet the conditions before they can continue to execute ,Python Provides threading.Condition Object is used to support conditional variable threads , In addition to providing RLock() or Lock() Out of the way , It also provides wait()、notify()、notifyAll() Method .
lock_con=threading.Condition([Lock/Rlock]): Locks are optional , Do not pass in locks , Object automatically creates a RLock().
application : Used for information exchange between threads
wait(): Called when conditions are not met , The thread releases the lock and enters a wait block ;
notify(): Call after condition creation , Notify the wait pool to activate a thread ;
notifyAll(): Call after condition creation , Notify the wait pool to activate all threads
import threading,time
from random import randint
class Producer(threading.Thread):
def run(self):
global L
while True:
val=randint(0,100)
print(' producer ',self.name,":Append"+str(val),L)
if lock_con.acquire():
L.append(val)
lock_con.notify() # notify No ability to release lock , So you need to release it manually
lock_con.release() # Release the lock manually
time.sleep(3)
class Consumer(threading.Thread):
def run(self):
global L
while True:
lock_con.acquire() # Get the lock back
if len(L)==0:
lock_con.wait() # wait Enter wait and release lock
print(' consumer ',self.name,":Delete"+str(L[0]),L)
del L[0]
lock_con.release()
time.sleep(0.25)
if __name__=="__main__":
L=[]
lock_con=threading.Condition()
threads=[]
for i in range(5):
threads.append(Producer())
threads.append(Consumer())
for t in threads:
t.start()
for t in threads:
t.join()
author : Small a ninety-seven
-------------------------------------------
Individuality signature : Everything is good in the end , If it's not good , That means things haven't come to the end yet ~
The copyright of this article belongs to the author 【 Small a ninety-seven 】, Welcome to reprint , However, this statement must be retained without the consent of the author , And in the article page obvious position gives the original link , Otherwise, the right to pursue legal responsibility is reserved !