2014-10-30 183 views
2

我在我的程序中遇到了死鎖問題。所以我一直在閱讀關於鎖的問題,但問題是大多數信息不一致或者沒有定義平臺。在Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex)最接受的回答說:遞歸和非遞歸鎖(互斥鎖)

由於遞歸互斥有主人翁意識,在線程 抓住互斥必須是釋放互斥鎖在同一個線程。在 非遞歸互斥體的情況下,沒有所有權意識,任何線程通常都可以釋放該互斥體,無論哪個線程本來是 接受互斥體。在許多情況下,這種類型的「互斥體」實際上更多的是信號量動作,在這種情況下,您不一定要使用互斥體作爲排斥設備,而是將其用作兩個或更多線程之間的同步或信號設備 。

在評論中,人們說這是不正確的,沒有提及它。所以...

1)如果我在線程A中鎖定了一個非遞歸互斥鎖,線程B可以解鎖它而無需獲取鎖?

2)如果線程A在一個非遞歸互斥體中進行了鎖定,並且線程B調用了鎖定,那麼線程B是否等待,直到鎖定被釋放以獲得鎖定或將拋出一個異常?這個遞歸互斥體的情況呢? (也可以在其他問題中進行討論,其中沒有可以作出的正確結論)

3)當使用遞歸鎖時,在進程終止時,是否必須釋放所有遞歸鎖? (取決於過程結束的地方沒有發生)

4)當謹慎使用遞歸和非遞歸鎖的組合時,我會注意哪些問題?

PS:只使用windows平臺和std::thread

回答

6

通過閱讀關於Reentrant Mutexes的維基,我認爲你會得到很大的幫助。我同意另一條線索中的評論;被接受的答案是錯誤的,或者至少非常不好地解釋它的觀點。

所有Mutexes都有一個所有權的概念。這就是他們與Semaphore不同的原因。鎖定互斥鎖的線程始終是需要解鎖的線程,這也是互斥鎖可能導致死鎖的原因之一,也是爲什麼它們能夠達到其預期目的(至相互排除訪問特定代碼塊)。

那麼Recursive/Reenrant和普通Mutex有什麼不同?一個遞歸互斥鎖可以被同一個線程多次鎖定。引用維基:

遞歸鎖(也稱爲遞歸線程互斥鎖)是允許線程遞歸獲取它所持有的同一個鎖的那些鎖。請注意,此行爲與普通鎖不同。在正常情況下,如果已經擁有正常鎖的線程嘗試再次獲取相同的鎖,則它會死鎖。

這是兩個互斥體類型之間的完整區別。實質上,如果你在遞歸方法中放置一個互斥鎖並且該方法在釋放互斥體之前遞歸,那麼你需要遞歸互斥鎖。否則,在第一次遞歸之後,將會發生即時死鎖,因爲無法再次獲取該鎖。

真的,這是只有原因使用遞歸互斥;大多數其他情況下,如果獲得相同線程而不釋放它的同一個線程,可能會重構爲正確獲取/釋放鎖而不需要遞歸互斥鎖。這樣做會更安全。遞歸函數自然會冒泡並釋放遞歸互斥鎖上的每個鎖,假設RAII,在其他情況下,您可能無法充分釋放互斥鎖並仍然陷入死鎖狀態。

因此,要回答你的具體問題:

  1. 沒有,除非你的互斥系統特別允許這
  2. 是在這兩種情況一般,雖然這同樣是特定至於阻斷/投擲互斥的實現。幾乎所有的系統我用過剛剛塊(這就是爲什麼非遞歸互斥體的僵局,如果在同一個線程鎖兩次沒有自由)
  3. 是,通常情況下,雖然通常他們會被釋放,假設正確RAII和進程終止正常。進程在保持鎖定狀態下非正常終止可能有點難以理解。
  4. 看到我上面的解釋。具體來說,提請注意從維基如下:

注意,遞歸鎖是說當且僅當它已經得到的次數相匹配的次數已釋放被釋放由所有者線程。

+0

_The接受的答案是錯誤的,或者至少是非常說明其一點,「鎖定互斥體可能比它與非遞歸互斥種所有者以外的其他線程來解鎖」非常poorly._它似乎你現在接受的答案可能會使用警衛。 – bvj 2017-12-27 02:13:46

0

真的,你應該寫一個簡單的程序來測試這些案件。

  1. 假設我們正確使用互斥鎖,另一個線程不應該解鎖互斥鎖。任何線程都可能解鎖互斥鎖。 (編輯:我從來沒有嘗試解鎖,另一個線程互斥這會破壞目的)這將導致競爭條件。

  2. 考慮代碼:

    void criticalSection(){ 
        pthread_mutex_lock(&mutex) 
        //dostuff 
        pthread_mutex_unlock(&mutex) 
    } 
    

    如果有兩個線程,線程A和B,和線程A首先進入的功能,它獲取鎖和確實的東西。如果線程B的狀態時,線程A還是在功能上,它會被鎖定。線程A執行時

    pthread_mutex_unlock(&mutex) 
    

    線程B現在將被「喚醒」並開始執行任務。

  3. 我想你可以做任何你想做的事情,但是如果有遞歸鎖意味着一個線程仍然在做某件事,你可能想等待它完成。

  4. 我想你正在尋找沒有競爭條件多線程應用程序。:-)

+2

幾乎總是在多線程中,最好是編寫正確的代碼,而不是基於行爲而是基於文檔。所以「你應該寫一個簡單的程序來測試這些情況」恕我直言是一個不好的建議,因爲它很容易引起誤解。 – Slava 2014-10-30 14:49:54

+0

那麼,在這種情況下,只是看看另一個線程是否可以解鎖鎖定的互斥鎖。但是,如果這甚至是可能的,它不應該發生在你的代碼 – 2014-10-30 14:55:25

+0

「這只是一個看看另一個線程是否可以解鎖鎖定互斥體的問題」,以防止旋轉鎖定,它可以很容易地在測試程序中「工作」。這並不意味着它應該以這種方式使用。 – Slava 2014-10-30 15:07:17

1

你指的是POSIX互斥體討論,但Windows不支持POSIX,你使用C++標準線程原語,細節可能有所不同。

所以最好先檢查標準庫文件,例如C++標準爲unlock明確規定:

要求:調用線程將擁有互斥。

1

以下是在Linux的pthread_mutex_lock手冊頁,

型pthread_mutex_t的變量也可以使用常量PTHREAD_MUTEX_INITIALIZER(快速互斥),THREAD_RECURSIVE_MUTEX_INITIALIZER_NP(遞歸互斥)和PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP靜態初始化, (用於錯誤檢查互斥體)。

error checking'' and遞歸'互斥體,調用pthread_mutex_unlock實際上在檢查該互斥鎖於入口的運行時間,而且它是由現在要求調用pthread_mutex_unlock相同的線程鎖定。如果這些條件不符合,則返回錯誤代碼並且互斥量保持不變。 ``快速''互斥鎖不執行此類檢查,因此允許鎖定的互斥鎖由非所有者的線程解鎖。這是不可移動的行爲,不能依賴。

看來,