1、關於死鎖。python
死鎖,就是當多個進程或者線程在執行的過程當中,因爭奪共享資源而形成的一種互相等待的現象,一旦產生了死鎖,不加人工處理,程序會一直等待下去,這也被稱爲死鎖進程。ide
下面是一個產生「死鎖」現象的例子:ui
import threading線程
import time對象
lock_a = threading.Lock()遞歸
lock_b = threading.Lock()進程
class test_thread(threading.Thread):資源
def __init__(self):get
super(test_thread,self).__init__()it
def run(self):
self.fun1()
self.fun2()
def fun1(self):
lock_a.acquire()
print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())
lock_b.acquire()
print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())
lock_b.release()
lock_a.release()
def fun2(self):
lock_b.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
time.sleep(0.2)
lock_a.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
lock_a.release()
lock_b.release()
if __name__ == "__main__":
print "start---------------------------%s"%(time.time())
for i in range(0, 10):
my_thread = test_thread()
my_thread.start()
輸出執行結果:
start---------------------------1494682814.1
I am Thread-1 , get res: ResA---1494682814.1
I am Thread-1 , get res: ResB---1494682814.1
I am Thread-1 , get res: ResB---1494682814.1
I am Thread-2 , get res: ResA---1494682814.1
下面來分析代碼,爲何會產生死鎖:
開了10個線程,首先確定會有一個線程去拿到lock_a這把鎖,其他的線程只能阻塞,一直等到lock_a這把鎖被釋放,而後這個線程又得到了一把鎖就是lock_b,執行完了一條print操做後,釋放lock_b這把鎖,此時,其餘的線程仍是沒有辦法去執行func1裏面的資源,釋放了lock_b這把鎖後,緊接着lock_a也被釋放了,此時,下一個線程就能夠去執行func1中的資源了,接下來,線程1執行到了func2拿到了lock_b這把鎖,而後執行一個print輸出,I am Thread-1 , get res: ResB---1494682814.1,sleep0.2秒,在第一個線程sleep的過程當中第二個線程開始執行,第二個線程在執行func1的時候,首先拿到了lock_a這把鎖,執行了下面的print語句,I am Thread-2 , get res: ResA---1494682814.1,此時的狀況就是線程1拿到了lock_b這把鎖,線程2拿到了lock_a這把鎖,那麼問題來了,線程2若是想繼續執行func1後面的內容,須要去得到lock_b這把鎖,而此時lock_b鎖已經被線程1拿走了(線程1執行到了func2拿走了lock_b鎖,而且還沒被釋放~),線程1,sleep0.2秒後,須要繼續執行func2中的內容,此時,須要拿到lock_a鎖,(lock_a鎖,被線程2執行func1的時候拿走了,而且沒有被釋放~),如今的狀況就是線程1須要lock_a鎖,可是lock_a在線程2手裏,線程2須要lock_b鎖,可是lock_b鎖在線程1手裏~雙方都沒有辦法執行到後面的釋放操做。
這也就至關於兩我的要作交易,甲手裏有蘋果乙手裏有菠蘿,甲想吃乙手裏的菠蘿,乙想吃甲手裏的蘋果,可是誰都不肯意先把本身手裏的東西先給對方~因此程序就會一直卡在這。
這就是死鎖造成的原理。
2、遞歸鎖。
解決死鎖問題有一個特別有效的方法,就是遞歸鎖.
遞歸鎖與普通的互斥鎖最大的不一樣就是,一個鎖的對象內部,維護了一個計數器,這個計數器的初始值是0,當一個線程acquire一次這個鎖時,內部計數器+1,可是,這把鎖的計數器一旦大於0,其餘的線程是沒法拿到這把鎖的,只有當前線程能夠拿。
(當前線程acquire一次,計數器+1,release一次計數器-1,因此,當前的線程想要完全釋放掉遞歸鎖,acquire多少次,就要release多少次!!!)
(這個計數器,就是遞歸鎖中的count。)
還拿剛纔的那段產生死鎖的代碼來舉例:
import threading
import time
r_lock = threading.RLock() #RLock是用來產生遞歸鎖的一個類,產生一個遞歸鎖。
class test_thread(threading.Thread):
def __init__(self):
super(test_thread,self).__init__()
def run(self):
self.fun1()
self.fun2()
def fun1(self):
r_lock.acquire() #count+1
print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())
r_lock.acquire() #count再+1
print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())
r_lock.release() #count -1
r_lock.release() #count 再-1
def fun2(self):
r_lock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
time.sleep(0.2)
r_lock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
r_lock.release()
r_lock.release()
if __name__ == "__main__":
print "start---------------------------%s"%(time.time())
r_lock = threading.RLock()
for i in range(0, 10):
my_thread = test_thread()
my_thread.start()
輸出結果:
start---------------------------1494687542.43
I am Thread-1 , get res: ResA---1494687542.43
I am Thread-1 , get res: ResB---1494687542.43
I am Thread-1 , get res: ResB---1494687542.43
I am Thread-1 , get res: ResA---1494687542.63
I am Thread-2 , get res: ResA---1494687542.63
I am Thread-2 , get res: ResB---1494687542.63
I am Thread-2 , get res: ResB---1494687542.63
I am Thread-2 , get res: ResA---1494687542.83
I am Thread-4 , get res: ResA---1494687542.83
I am Thread-4 , get res: ResB---1494687542.83
I am Thread-4 , get res: ResB---1494687542.83
I am Thread-4 , get res: ResA---1494687543.04
I am Thread-6 , get res: ResA---1494687543.04
I am Thread-6 , get res: ResB---1494687543.04
I am Thread-6 , get res: ResB---1494687543.04
I am Thread-6 , get res: ResA---1494687543.24
I am Thread-8 , get res: ResA---1494687543.24
I am Thread-8 , get res: ResB---1494687543.24
I am Thread-8 , get res: ResB---1494687543.24
I am Thread-8 , get res: ResA---1494687543.44
I am Thread-10 , get res: ResA---1494687543.44
I am Thread-10 , get res: ResB---1494687543.44
I am Thread-10 , get res: ResB---1494687543.44
I am Thread-10 , get res: ResA---1494687543.65
I am Thread-5 , get res: ResA---1494687543.65
I am Thread-5 , get res: ResB---1494687543.65
I am Thread-5 , get res: ResB---1494687543.65
I am Thread-5 , get res: ResA---1494687543.85
I am Thread-9 , get res: ResA---1494687543.85
I am Thread-9 , get res: ResB---1494687543.85
I am Thread-9 , get res: ResB---1494687543.85
I am Thread-9 , get res: ResA---1494687544.06
I am Thread-7 , get res: ResA---1494687544.06
I am Thread-7 , get res: ResB---1494687544.06
I am Thread-7 , get res: ResB---1494687544.06
I am Thread-7 , get res: ResA---1494687544.26
I am Thread-3 , get res: ResA---1494687544.26
I am Thread-3 , get res: ResB---1494687544.26
I am Thread-3 , get res: ResB---1494687544.26
I am Thread-3 , get res: ResA---1494687544.46
從上面的例子來看,死鎖的問題被完美的解決掉了。
最後,總結下遞歸鎖:
在python中,若是同一個線程須要屢次去訪問同一個共享資源,這個時候,就可使用遞歸鎖(RLock),遞歸鎖的內部,維護了一個Lock對象和一個counter計數變量,counter記錄了acquire的次數,從而使得資源能夠被屢次require。直到一個線程全部的acquire都被release,其餘的線程才能得到資源。
因此說RLock能夠徹底代替Lock,能用遞歸鎖儘可能用遞歸鎖!