Lock

Python's GIL: Global Interpreter Lock (Global Interpreter Lock) When executing multi-threading, at most one thread is executed by the CPU at the same time. If you want to achieve parallelism, you can use multi-process.

Python is suitable for IO-intensive situations, but not very suitable for computationally intensive scenarios, because switching between threads when performing computations takes time.

There are three situations below

import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#result is 0
import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    time.sleep(1)
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#result is 29
import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    time.sleep(0.001)
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#Every time the result is different

In the first case, because the running time of the program is too short, there is no need to switch between threads, which is equivalent to decrementing 1 each time the threads are executed one by one, so the result is 0. It can also be seen from the output AB situation.

In the second case, when sleep is encountered, the interpreter will switch the task of the thread, and the sleep time of 1 second is too long, so that the temp of each thread gets the initial value of num 30, and then all the operations are performed to reduce the result by 1. 29 is assigned to num, so it is best that num is equal to 29. It can also be seen from the output of AB

In the third case, the thread is switched when sleep is encountered, but due to the short sleep time, only part of the thread temp gets the initial value of num 30, and then the program switches back to the original thread to subtract 1, and assign the result to num, The temp of the latter part of the thread obtains a new num value, and then switches back to subtract 1, and then the latter thread obtains a new num value, and so on until the end. Because the time point of each switch is not unique, the final num value is also different. It can also be seen from the case of AB.

To get the result of 0, you can also use Lock lock

import threading
import time

def sub():
    global num
    lock.acquire()#Acquire the lock
    temp=num
    print('A',end='|')
    time.sleep(0.01)
    num=temp-1
    lock.release()#release the lock
    print('B',end='|')

lock=threading.Lock()

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

After the thread acquires the lock, only the program under the lock can be executed, and other threads cannot be executed until the lock is released

deadlock situation

import threading
import time
class MyThread(threading.Thread):
    def actionA (self):

        A.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(2)

        B.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(1)

        B.release()
        A.release()

    def actionB(self):

        B.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(2)

        A.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(1)

        A.release()
        B.release()

    def run(self):
        self.actionA ()
        self.actionB()



if __name__ == '__main__':
    A=threading.Lock()
    B=threading.Lock()

    l=[]
    for i in range(5):
        t=MyThread()
        l.append(t)
        t.start()
    for t in l:
        t.join()

print('ending...')
The result is
Thread-1 gotA Thu Apr 19 20:14:41 2018#Thread 1 starts to execute actionA and acquires A lock
Thread-1 gotB Thu Apr 19 20:14:43 2018#Thread 1, get B lock, and release B lock and A lock after 1 second
Thread-1 gotB Thu Apr 19 20:14:44 2018#Thread 1 executes actionB, obtains the B lock, encounters sleep, and performs thread switching
Thread-2 gotA Thu Apr 19 20:14:44 2018#Thread 2 starts to execute actionA and acquires A lock
#Thread 1 must obtain A lock if it wants to continue running, and thread 2 must obtain B lock if it wants to continue running, but the locks are all in the hands of the other party, so the program cannot continue to execute, which is a deadlock

To solve the above problems, recursive locks can be used

import threading
import time
class MyThread(threading.Thread): #Use classes to implement multi-threading, you must inherit threading.Thread, and you must define the run function, because t.start() is to call the run function
    def actionA (self):

        r_lock.acquire()#count=1
        print(self.name,'gotA',time.ctime())
        time.sleep(2)

        r_lock.acquire()#count=2
        print(self.name,'gotB',time.ctime())
        time.sleep(1)

        r_lock.release()#count=1
        r_lock.release()#count=0

    def actionB(self):

        r_lock.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(2)

        r_lock.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(1)

        r_lock.release()
        r_lock.release()

    def run(self):
        self.actionA ()
        self.actionB()



if __name__ == '__main__':
    r_lock=threading.RLock()

    l=[]
    for i in range(5):
        t=MyThread()
        l.append(t)
        t.start()
    for t in l:
        t.join()

print('ending...')

There is a counter in the recursive lock. When the recursive lock is acquired once, the counter is incremented by 1, and the counter is decremented by 1 once it is released.

Only when the counter is equal to 0 can it be acquired by other threads. When it is greater than 0, only the thread that currently acquires the lock can be executed. Until the counter becomes 0 again, it is possible for other threads to acquire the recursive lock, which solves the deadlock situation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325838756&siteId=291194637