11.python並發入門(part4 死結與遞迴鎖)

來源:互聯網
上載者:User

標籤:python   lock   線程   互斥鎖   rlock   

一、關於死結。

死結,就是當多個進程或者線程在執行的過程中,因爭奪共用資源而造成的一種互相等待的現象,一旦產生了死結,不加人工處理,程式會一直等待下去,這也被稱為死結進程。

下面是一個產生“死結”現象的例子:

import threading

import time

lock_a = threading.Lock()

lock_b = threading.Lock()

class test_thread(threading.Thread):

    def __init__(self):

        super(test_thread,self).__init__()

    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手裡~雙方都沒有辦法執行到後面的釋放操作。

這也就相當於兩個人要做交易,甲手裡有蘋果乙手裡有菠蘿,甲想吃乙手裡的菠蘿,乙想吃甲手裡的蘋果,但是誰都不願意先把自己手裡的東西先給對方~所以程式就會一直卡在這。

這就是死結形成的原理。


二、遞迴鎖。

解決死結問題有一個特別有效方法,就是遞迴鎖.

遞迴鎖與普通的互斥鎖最大的不同就是,一個鎖的對象內部,維護了一個計數器,這個計數器的初始值是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,能用遞迴鎖盡量用遞迴鎖!

本文出自 “reBiRTH” 部落格,請務必保留此出處http://suhaozhi.blog.51cto.com/7272298/1925393

11.python並發入門(part4 死結與遞迴鎖)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.