防止死鎖的加鎖機制

問題:  

多線程中一個線程須要一次得到多個鎖,怎麼才能實現不會出現死鎖的狀況。  多線程

解決方案:

在多線程程序中,死鎖問題很大一部分是因爲線程同時獲取多個鎖形成的。舉個例子:一個線程獲取了第一個鎖,而後在獲取第二個鎖的 時候發生阻塞,那麼這個線程就可能阻塞其餘線程的執行,從而致使整個程序假死。 解決死鎖問題的一種方案是爲程序中的每個鎖分配一個惟一的id,而後只容許按照升序規則來使用多個鎖,這個規則使用上下文管理器 是很是容易實現的,示例以下:函數

 1 import threading
 2 from contextlib import contextmanager
 3 
 4 _local = threading.local()  # 獲取一個_local對象
 5 
 6 @contextmanager
 7 def acquire(*locks):
 8     locks = sorted(locks, key=lambda x:id(x))  # 把傳入的鎖排個序
 9 
10     acquired = getattr(_local, "acquired", [])  # 看一下當前線程有沒有acquired屬性,沒有就返回一個列表
11     if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):  # 若是不是空列表就有這個屬性,
12         # 若是當前線程的acquired列表中最大的鎖比傳入的最小的鎖要大的話,說明傳入的鎖中有已經被acquire的鎖了,不能再進行acquire
13         raise RuntimeError("Lock Order Violation")
14 
15     acquired.extemd(locks)  # 若是沒有出現已經被acquire的鎖,咱們就把這些鎖和當前線程已經有的鎖放在一塊兒
16     _local.acquired = acquired
17 
18     try:
19         for lock in locks:  # 而後按照順序依次地acquire鎖
20             lock.acquire()
21         yield
22 
23     finally:
24         for lock in reversed(locks):  # 上下文結束以後按照上鎖的順序的逆序進行解鎖
25             lock.release()
26         del acquired[-len(locks):]  # 而後刪除掉這些lock

使用方法,能夠按照正常途徑建立一個鎖對象,但不管是單個鎖仍是多個鎖中都使用 acquire() 函數來申請鎖, 示例以下:ui

 1 import threading
 2 x_lock = threading.Lock()
 3 y_lock = threading.Lock()
 4 
 5 def thread_1():
 6     while True:
 7         with acquire(x_lock, y_lock):
 8             print('Thread-1')
 9 
10 def thread_2():
11     while True:
12         with acquire(y_lock, x_lock):
13             print('Thread-2')
14 
15 t1 = threading.Thread(target=thread_1)
16 t1.daemon = True
17 t1.start()
18 
19 t2 = threading.Thread(target=thread_2)
20 t2.daemon = True
21 t2.start()

討論

死鎖是每個多線程程序都會面臨的一個問題(就像它是每一本操做系統課本的共同話題同樣)。根據經驗來說,儘量保證每個 線程只能同時保持一個鎖,這樣程序就不會被死鎖問題所困擾。一旦有線程同時申請多個鎖,一切就不可預料了。spa

死鎖的檢測與恢復是一個幾乎沒有優雅的解決方案的擴展話題。一個比較經常使用的死鎖檢測與恢復的方案是引入看門狗計數器。當線程正常 運行的時候會每隔一段時間重置計數器,在沒有發生死鎖的狀況下,一切都正常進行。一旦發生死鎖,因爲沒法重置計數器致使定時器 超時,這時程序會經過重啓自身恢復到正常狀態。操作系統

避免死鎖是另一種解決死鎖問題的方式,在進程獲取鎖的時候會嚴格按照對象id升序排列獲取,通過數學證實,這樣保證程序不會進入 死鎖狀態。避免死鎖的主要思想是,單純地按照對象id遞增的順序加鎖不會產生循環依賴,而循環依賴是 死鎖的一個必要條件,從而避免程序進入死鎖狀態。線程

相關文章
相關標籤/搜索