首頁 常見問題 如何避免死鎖?

如何避免死鎖?

Jun 24, 2020 pm 02:33 PM
死鎖

如何避免死鎖?

避免死鎖的方法:

當兩個執行緒互相等待對方釋放資源時,就會發生死鎖。 Python 解釋器沒有監測,也不會主動採取措施來處理死鎖情況,所以在進行多執行緒程式設計時應該採取措施避免死鎖。

一旦出現死鎖,整個程式既不會發生任何異常,也不會給予任何提示,只是所有執行緒都處於阻塞狀態,無法繼續。

死鎖是很容易發生的,尤其是在系統中出現多個同步監視器的情況下,如下程序將會出現死鎖:

import threading
import time
class A:
    def __init__(self):
        self.lock = threading.RLock()
    def foo(self, b):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了A实例的foo()方法" )     # ①
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用B实例的last()方法")   # ③
            b.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了A类的last()方法内部")
        finally:
            self.lock.release()
class B:
    def __init__(self):
        self.lock = threading.RLock()
    def bar(self, a):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了B实例的bar()方法" )   # ②
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用A实例的last()方法")  # ④
            a.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了B类的last()方法内部")
        finally:
            self.lock.release()
a = A()
b = B()
def init():
    threading.current_thread().name = "主线程"
    # 调用a对象的foo()方法
    a.foo(b)
    print("进入了主线程之后")
def action():
    threading.current_thread().name = "副线程"
    # 调用b对象的bar()方法
    b.bar(a)
    print("进入了副线程之后")
# 以action为target启动新线程
threading.Thread(target=action).start()
# 调用init()函数
init()
登入後複製

執行上面程序,將會看到如圖1 所示的效果。

如何避免死鎖?

圖1 死鎖效果

從圖1 可以看出,程式既無法向下執行,也不會拋出任何異常,就一直「僵持」著。究其原因,是因為上面程式中 A 物件和 B 物件的方法都是執行緒安全的方法。

程式中有兩個執行緒執行,副執行緒的執行緒執行體是 action() 函數,主執行緒的執行緒執行體是 init() 函數(主程式呼叫了 init() 函數)。其中在 action() 函數中讓 B 物件呼叫 bar() 方法,而在 init() 函數中讓 A 物件呼叫 foo() 方法。

圖1 顯示action() 函數先執行,呼叫了B 物件的bar() 方法,在進入bar() 方法之前,該執行緒對B 物件的Lock 加鎖(當程式執行到② 號程式碼時,副線程暫停0.2s);CPU 切換到執行另一個線程,讓A 物件執行foo() 方法,所以看到主線程開始執行A 實例的foo() 方法,在進入foo() 方法之前,此執行緒對A 物件的Lock 加鎖(當程式執行到① 號程式碼時,主執行緒也暫停0.2s)。

接下來副線程會先醒過來,繼續向下執行,直到執行到④ 號程式碼處希望呼叫A 物件的last() 方法(在執行該方法之前,必須先對A 物件的Lock加鎖),但此時主執行緒正保持著A 物件的Lock 的鎖定,所以副執行緒被阻塞。

接下來主執行緒應該也醒過來了,繼續向下執行,直到執行到③ 號程式碼處希望呼叫B 物件的last() 方法(在執行該方法之前,必須先對B 物件的Lock 加鎖),但此時副執行緒並沒有釋放對B 物件的Lock 的鎖定。

至此,就出現了主執行緒保持著A 物件的鎖,等待對B 物件加鎖,而副執行緒保持著B物件的鎖,等待對A 物件加鎖,兩個執行緒互相等待對方先釋放鎖,所以就出現了死鎖。

死鎖是不應該在程式中出現的,在編寫程式時應該盡量避免出現死鎖。 下面有幾種常見的方式用來解決死鎖問題:

  1. #避免多次鎖定。盡量避免同一個執行緒對多個 Lock 進行鎖定。例如上面的死鎖程序,主線程要對 A、B 兩個物件的 Lock 進行鎖定,副線程也要對 A、B 兩個物件的 Lock 進行鎖定,這就埋下了導致死鎖的隱患。

  2. 具有相同的加鎖順序。如果多個執行緒需要對多個 Lock 進行鎖定,則應該保證它們以相同的順序請求加鎖。例如上面的死鎖程序,主執行緒先對 A 物件的 Lock 加鎖,再對 B 物件的 Lock 加鎖;而副執行緒則先對 B 物件的 Lock 加鎖,再對 A 物件的 Lock 加鎖。這種加鎖順序很容易形成嵌套鎖定,進而導致死鎖。如果讓主執行緒、副執行緒依照相同的順序加鎖,就可以避免這個問題。

  3. 使用定時鎖定。程式在呼叫 acquire() 方法加鎖時可指定 timeout 參數,該參數指定超過 timeout 秒後會自動釋放對 Lock 的鎖定,這樣就可以解開死鎖了。

  4. 死鎖偵測。死鎖檢測是一種依靠演算法機制​​來實現的死鎖預防機制,它主要是針對那些不可能實現按序加鎖,也不能使用定時鎖的場景的。

#

以上是如何避免死鎖?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何處理C++開發中的死鎖問題 如何處理C++開發中的死鎖問題 Aug 22, 2023 pm 02:24 PM

如何處理C++開發中的死鎖問題死鎖是多執行緒程式設計中常見的問題之一,尤其是在使用C++進行開發時更容易遇到。當多個執行緒互相等待對方持有的資源時,就可能發生死鎖問題。如果不及時處理,死鎖不僅會導致程式卡死,還會影響系統的效能和穩定性。因此,學習如何處理C++開發中的死鎖問題是非常重要的。一、理解死鎖的原因要解決死鎖問題,首先要了解死鎖產生的原因。死鎖通常發生在以

C++ 多執行緒程式設計中死鎖預防與偵測機制 C++ 多執行緒程式設計中死鎖預防與偵測機制 Jun 01, 2024 pm 08:32 PM

多執行緒死鎖預防機制包括:1.鎖順序;2.測試並設定。偵測機制包括:1.超時;2.死鎖偵測器。文章舉例共享銀行帳戶,透過鎖定順序避免死鎖,為轉帳函數先請求轉出帳戶再請求轉入帳戶的鎖。

golang函數並發控制中死鎖與飢餓的預防與解決 golang函數並發控制中死鎖與飢餓的預防與解決 Apr 24, 2024 pm 01:42 PM

Go中死鎖與飢餓:預防與解決死鎖:協程相互等待而無法進行的操作,使用runtime.SetBlockProfileRate函數偵測。預防死鎖:使用細粒度加鎖、逾時、無鎖定資料結構,防止死鎖。飢餓:協程持續無法取得資源,使用公平鎖防止飢餓。公平鎖實踐:創建公平鎖並等待協程嘗試獲取鎖的時間最長的優先獲取鎖。

如何調試 C++ 程式中的死鎖? 如何調試 C++ 程式中的死鎖? Jun 03, 2024 pm 05:24 PM

死鎖是一種並發程式設計中的常見錯誤,發生在多個執行緒等待彼此持有的鎖時。可以透過使用調試器檢測死鎖,分析線程活動並識別涉及的線程和鎖,從而解決死鎖。解決死鎖的方法包括避免循環依賴、使用死鎖偵測器和使用逾時。在實踐中,透過確保執行緒以相同的順序取得鎖或使用遞歸鎖或條件變數可以避免死鎖。

Go開發中解決死鎖的方法 Go開發中解決死鎖的方法 Jun 30, 2023 pm 04:58 PM

解決Go語言開發中的死鎖問題的方法Go語言是一種開源的靜態類型編譯型語言,被廣泛應用於並發程式設計。然而,由於Go語言的並發模型的特性,開發者在編寫並發程式時常常會遇到死鎖問題。本文將介紹一些解決Go語言開發中死鎖問題的方法。首先,我們需要了解何為死鎖。死鎖是指多個並發任務因互相等待對方釋放資源而無法繼續執行的情況。在Go語言中,死鎖問題通常是由於對資源的競爭或

C++ 函式如何解決並發程式設計中的死鎖問題? C++ 函式如何解決並發程式設計中的死鎖問題? Apr 26, 2024 pm 01:18 PM

在C++中,使用互斥函數可以解決多執行緒並發程式設計中的死鎖問題。具體步驟如下:建立一個互斥量;當執行緒需要存取共享變數時,獲得互斥;修改共享變數;釋放互斥。這樣可以確保任何時刻只有一個執行緒存取共享變量,有效防止死鎖。

如何解決Go語言中的死鎖問題? 如何解決Go語言中的死鎖問題? Oct 08, 2023 pm 05:07 PM

如何解決Go語言中的死鎖問題? Go語言具有並發程式設計的特性,可以透過使用goroutine和channel來實現並發操作。然而,在並發程式設計中,死鎖是一個常見的問題。當goroutine之間相互依賴彼此的資源,並且在存取這些資源時產生了循環依賴關係,就可能導致死鎖的發生。本文將介紹如何解決Go語言中的死鎖問題,並提供具體的程式碼範例。首先,讓我們來了解一下什麼是

Python 中的並發程式設計難題:與死鎖和競態條件作戰 Python 中的並發程式設計難題:與死鎖和競態條件作戰 Feb 19, 2024 pm 02:40 PM

死鎖死鎖是指多個執行緒相互等待資源,從而形成一個循環,最終導致所有執行緒都阻塞。在python中,死鎖通常發生在對多個鎖或互斥量以錯誤順序進行鎖定時。範例:importthreading#兩個執行緒共用兩個鎖定lock1=threading.Lock()lock2=threading.Lock()defthread1_func():lock1.acquire()lock2.acquire()#做一些操作lock2.release()lock1. release()defthread2_func():loc