Synchronization lock
import time, threading def addNum(): global num num -= 1 num = 100 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: t.join() print('final num:', num) //Run result: final num: 0
import time, threading def addNum(): global num #num -= 1 tmp = num time.sleep(0.00001) num = tmp - 1 num = 100 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: t.join() print('final num:', num) //Run result: final num: 93 //or final num: 91 //or final num: 94
Reason:
In the first program, num -= 1 is written so that the program executes too quickly (within the time of cup switching)
In the second program, num -= 1 was added to sleep time, and 100 threads switched before execution completed, resulting in the global num not returning properly.Quote the picture of the Great God and find that it summarizes very well:
In the example above, using the join method will stop the entire thread, causing the serialization, losing the meaning of multithreading, and we only need to execute the serialization when it comes to computing public data.
Computing common data using synchronous lock processing
import time, threading def addNum(): global num lock.acquire() tmp = num time.sleep(0.00001) num = tmp - 1 lock.release() num = 100 lock = threading.Lock() thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: t.join() print('final num:', num) //Operation result: final num: 0
Thread Deadlocks and Recursive Locks
import threading, time 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 t in threads: t.start() for t in threads: t.join() #Run result: Thread-1 gotlockA Sat Jul 28 15:09:31 2018 Thread-1 gotlockB Sat Jul 28 15:09:34 2018 Thread-1 gotlockB Sat Jul 28 15:09:34 2018 Thread-2 gotlockA Sat Jul 28 15:09:34 2018
Use recursive locks
import threading, time class myThread(threading.Thread): def doA(self): lock.acquire() print(self.name, "gotlockA", time.ctime()) time.sleep(3) lock.acquire() 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 t in threads: t.start() for t in threads: t.join() //Run result: Thread-1 gotlockA Sat Jul 28 15:19:35 2018 Thread-1 gotlockB Sat Jul 28 15:19:38 2018 Thread-1 gotlockB Sat Jul 28 15:19:38 2018 Thread-1 gotlockA Sat Jul 28 15:19:40 2018 Thread-3 gotlockA Sat Jul 28 15:19:40 2018 Thread-3 gotlockB Sat Jul 28 15:19:43 2018 Thread-3 gotlockB Sat Jul 28 15:19:43 2018 Thread-3 gotlockA Sat Jul 28 15:19:45 2018 Thread-5 gotlockA Sat Jul 28 15:19:45 2018 Thread-5 gotlockB Sat Jul 28 15:19:48 2018 Thread-5 gotlockB Sat Jul 28 15:19:48 2018 Thread-5 gotlockA Sat Jul 28 15:19:50 2018 Thread-4 gotlockA Sat Jul 28 15:19:50 2018 Thread-4 gotlockB Sat Jul 28 15:19:53 2018 Thread-4 gotlockB Sat Jul 28 15:19:53 2018 Thread-4 gotlockA Sat Jul 28 15:19:55 2018 Thread-2 gotlockA Sat Jul 28 15:19:55 2018 Thread-2 gotlockB Sat Jul 28 15:19:58 2018 Thread-2 gotlockB Sat Jul 28 15:19:58 2018 Thread-2 gotlockA Sat Jul 28 15:20:00 2018
Semaphore
Semaphore or BoundedSemaphore manages a built-in counter used to control the concurrency of threads. Whenever acquire() is called, -1 is called, release() is called, +1 is called, and the counter cannot be less than 0. When the counter is 0, acquire() blocks the thread to a synchronous lock until release() is called by another thread.(similar to the concept of parking spaces).
The only difference between BoundedSemaphore and Semaphore is that the former checks if the value of the counter exceeds the initial value of the counter when release() is called and throws an exception if it exceeds.
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(20): thrs.append(myThread()) for t in thrs: t.start() #Run result: Thread-1 Thread-2 Thread-3 Thread-4 Thread-5 Thread-6 Thread-7 Thread-9 Thread-10 Thread-8 Thread-11 Thread-13 Thread-14 Thread-12 Thread-15 Thread-18 Thread-16 Thread-17 Thread-19 Thread-20
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.BoundedSemaphore(5) thrs = [] for i in range(20): thrs.append(myThread()) for t in thrs: t.start() #Run result: Thread-1 Thread-2 Thread-3 Thread-4 Thread-5 Thread-6 Thread-8 Thread-10 Thread-9 Thread-7 Thread-12 Thread-14 Thread-15 Thread-13 Thread-11 Thread-16 Thread-17 Thread-20 Thread-19 Thread-18