死锁的原因及解决办法RLock递归锁
死锁
说到死锁,可以讲一个科学家吃面的问题:
有几个科学家在一张桌子旁,桌子上只有一把筷子和一碗面,我们将面和筷子都加锁。这是可能会导致一个科学家抢到面,另一个科学家抢到筷子,这是就全部阻塞了,这就是死锁了。
如下代码:
from threading import Thread, Lock, RLock import time # 这个函数,先让拿筷子,再拿面条, def eat1(args, Chopsticks_lock, Noodles_lock): Chopsticks_lock.acquire() # 拿钥匙 print('%s拿到了筷子'%args) Noodles_lock.acquire() # 拿钥匙 print('%s拿到了面条'%args) print('%s吃到了面条'%args) Noodles_lock.release() # 还钥匙 Chopsticks_lock.release() # 还钥匙 # 这个函数,先让那面条,再让拿筷子。 def eat2(args, Chopsticks_lock, Noodles_lock): Noodles_lock.acquire() print('%s拿到了面条' % args) time.sleep(0.1) # 让睡0.1面,这样更容易,一个线程拿到面条,一个线程拿到筷子,出现阻塞,出现死锁。 Chopsticks_lock.acquire() print('%s拿到了筷子'%args) print('%s吃到了面条'%args) Noodles_lock.release() Chopsticks_lock.release() # 创建两个锁,分别给面条和筷子加锁。 Chopsticks_lock = Lock() Noodles_lock = Lock() Thread(target=eat1, args=('小明', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat2, args=('小红', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat1, args=('小兰', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat2, args=('小军', Chopsticks_lock, Noodles_lock)).start()
打印结果:
小明拿到了筷子
小明拿到了面条
小明吃到了面条
小红拿到了面条
小兰拿到了筷子
看到小红拿到了面条,而小兰拿到了筷子,他们都需要对方拿到的资源来完成吃面条的整个过程,但是线程未使用完资源之前,不可被剥夺,并且线程拿不到需要的资源就会阻塞,就造成了死锁的情况。
如何解决?
引入递归锁。
递归锁RLock
from threading import Thread, Lock, RLock import time def eat1(args, Chopsticks_lock, Noodles_lock): Chopsticks_lock.acquire() print('%s拿到了筷子'%args) Noodles_lock.acquire() print('%s拿到了面条'%args) print('%s吃到了面条'%args) Noodles_lock.release() Chopsticks_lock.release() def eat2(args, Chopsticks_lock, Noodles_lock): Noodles_lock.acquire() print('%s拿到了面条' % args) time.sleep(0.1) Chopsticks_lock.acquire() print('%s拿到了筷子'%args) print('%s吃到了面条'%args) Noodles_lock.release() Chopsticks_lock.release() Chopsticks_lock = Noodles_lock = RLock() Thread(target=eat1, args=('小明', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat2, args=('小红', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat1, args=('小兰', Chopsticks_lock, Noodles_lock)).start() Thread(target=eat2, args=('小军', Chopsticks_lock, Noodles_lock)).start()
打印结果:
小明拿到了筷子
小明拿到了面条
小明吃到了面条
小红拿到了面条
小红拿到了筷子
小红吃到了面条
小兰拿到了筷子
小兰拿到了面条
小兰吃到了面条
小军拿到了面条
小军拿到了筷子
小军吃到了面条
值得注意的是,线程锁Lock是互斥锁,只有一把钥匙。而递归锁RLock是一个钥匙串的很多把钥匙,每个钥匙都可以开一把锁,但是只要一个进程拿到这串钥匙的其中一个钥匙,其他线程就拿不到钥匙了,这就是递归锁,就造成不了面条和筷子分别被两个线程拿到的情况了。
结束!