Mutex lock, deadlock and recursive lock, mutex lock Recursion
1. Mutex)
In the last section, we talked about thread security. Thread Synchronization ensures that multiple threads can securely access competing resources. The simplest synchronization mechanism is to introduce mutex locks. The mutex lock introduces a status for the resource: Locked/unlocked. When a thread wants to change the shared data, it first locks it. At this time, the resource status is "locked" and other threads cannot be changed until the thread releases resources, changes the resource status to "Unlocked", and other threads can lock the resource again. The mutex lock ensures that only one thread writes data at a time, thus ensuring data correctness in the case of multiple threads.
The threading module defines the Lock class to facilitate Lock handling:
# Create lock Lock = threading. lock () # lock. acquire (blocking = True, timeout =-1) # release Lock. release ()
Take the preceding example as an example:
# -*- coding: UTF-8 -*-import threadingclass MyThread(threading.Thread): def run(self): global n lock.acquire() n += 1 lock.release() print(self.name + ' set n to ' + str(n))n = 0lock = threading.Lock()if __name__ == '__main__': for i in range(5): t = MyThread() t.start() print('final num: %d' % n)
Output:
Thread-1 set n to 1Thread-2 set n to 2Thread-3 set n to 3Thread-4 set n to 4Thread-5 set n to 5final num: 5
After locking, we can ensure that the data is correct.
Ii. deadlock
A deadlock occurs when a resource is called multiple times, but the caller fails to release the resource multiple times, causing mutual waiting. If no external force is applied, they will not be able to proceed. It is said that the system is in a deadlock state or the system has a deadlock.
2.1 A thread is locked multiple times but not released
Import threadingclass MyThread (threading. thread): def run (self): global n1, n2 lock. acquire () # Lock n1 + = 1 print (self. name + 'set n1 to '+ str (n1) lock. acquire () # Lock n2 + = n1 print (self. name + 'set n2 to '+ str (n2) lock. release () lock. release () n1, n2 = 0, 0 lock = threading. lock () if _ name _ = '_ main _': thread_list = [] for I in range (5): t = MyThread () t. start () thread_list.append (t) for t in thread_list: t. join () print ('final num: % d, % d' % (n1, n2 ))
Result:
Thread-1 set n1 to 1 # will keep waiting
Deadlock caused by mutual calls between more than 2.2 programs
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return numdef run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.Lock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count())else: print('----all threads done---') print(num,num2)
Iii. Recursive locks
The above two problems can be solved using the python recursive lock.
# Recursive lock rlock = threading. RLOCK ()
RLock internally maintains a Lock and a counter variable. counter records the number of acquire times so that resources can be require multiple times. Resources can be obtained only when all acquire of a thread is release. Here we take example 1. If you use RLock instead of Lock, no deadlock will occur:
#-*-Coding: UTF-8-*-import threadingclass MyThread (threading. thread): def run (self): global n1, n2 lock. acquire () # Lock n1 + = 1 print (self. name + 'set n1 to '+ str (n1) lock. acquire () # Lock n2 + = n1 print (self. name + 'set n2 to '+ str (n2) lock. release () lock. release () n1, n2 = 0, 0 lock = threading. RLock () if _ name _ = '_ main _': thread_list = [] for I in range (5): t = MyThread () t. start () thread_list.append (t) for t in thread_list: t. join () print ('final num: % d, % d' % (n1, n2 ))
Output:
Thread-1 set n1 to 1Thread-1 set n2 to 1Thread-2 set n1 to 2Thread-2 set n2 to 3Thread-3 set n1 to 3Thread-3 set n2 to 6Thread-4 set n1 to 4Thread-4 set n2 to 10Thread-5 set n1 to 5Thread-5 set n2 to 15final num:5 ,15