天天看點

死鎖的原因及解決辦法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是一個鑰匙串的很多把鑰匙,每個鑰匙都可以開一把鎖,但是隻要一個程序拿到這串鑰匙的其中一個鑰匙,其他線程就拿不到鑰匙了,這就是遞歸鎖,就造成不了面條和筷子分别被兩個線程拿到的情況了。

結束!