Python的GIL:全局解释锁(Global Interpreter Lock)在执行多线程时,同一时刻至多只有一个线程被CPU执行,如果要实现并行可以使用多进程。

Python适用于IO密集型情况,不太适用于计算密集型的场景,因为执行计算时线程间的切换会耗费时间。

下面有三种情况

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)

#结果为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)

#结果为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)

#每次的结果都不同

第一种情况,因为程序运行时间太短,所以线程间不用切换,相当于线程一个一个执行每次都减1故结果为0,从输出的AB情况也可以看出

第二种情况,当碰到sleep时解释器会进行线程的任务切换,而且sleep1秒的时间过长,导致每个线程的temp都拿到num的初始值30,然后全部进行减1操作将结果29又赋给num,所以最好num等于29,从输出的AB情况也可以看出

第三种情况,碰到sleep时开始切换线程,但是由于sleep时间短,只有部分线程temp拿到num的初始值30,然后程序又切回原来的线程进行减1运算,将结果赋给num,而后面的部分线程的temp获得新的num值,而后又切换回来做减1运算,然后后面的线程获得新的num值,如此往复直到结束。因为每次切换的时间点并不唯一,所以最后的num值也不一样。从AB的情况也可以看出。

要得到0的结果还可以用Lock锁

import threading
import time

def sub():
    global num
    lock.acquire()#获取锁
    temp=num
    print('A',end='|')
    time.sleep(0.01)
    num=temp-1
    lock.release()#释放锁
    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)

在线程获取锁之后,只能执行锁下面的程序,直到锁被释放,才能执行其他线程

死锁的情况

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...')
结果为
Thread-1 gotA Thu Apr 19 20:14:41 2018#线程1开始执行actionA,获得A锁
Thread-1 gotB Thu Apr 19 20:14:43 2018#线程1,获得B锁,并于1秒后释放B锁和A锁
Thread-1 gotB Thu Apr 19 20:14:44 2018#线程1执行actionB,获得B锁,碰到sleep,进行线程切换
Thread-2 gotA Thu Apr 19 20:14:44 2018#线程2开始执行actionA,获得A锁
#线程1要继续运行就要获得A锁,线程2要继续运行就要获得B锁,但是锁都到对方手中,所以造成程序无法继续执行,就是死锁

要解决以上问题可以用递归锁

import threading
import time
class MyThread(threading.Thread):#用类来实现多线程,必须继承threading.Thread,且必须定义run函数,因为t.start()就是调用run函数
    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...')

递归锁里有个计数器,当递归锁被acquire一次计数器就加一,release一次计数器就减1。

只有当计数器等于0时才能被其他的线程获得,大于0时只能执行现在获得锁的线程,直到计数器再次变为0时,其他线程才有可能获得递归锁,解决了死锁的情况

猜你喜欢

转载自blog.csdn.net/henry_lv/article/details/80009696