NPTL 執行緒同步方式

KingsLanding發表於2015-03-26

  NPTL提供了互斥體 pthread_mutex_t 型別進行執行緒同步,防止由於多執行緒併發對全域性變數造成的不正確操作。使用 pthread_mutext_t 對資料進行保護已經可以實現基本的資料同步,NPTL又提供了pthread_cond_t 條件變數與pthread_mutext_t一起使用實現高效的執行緒同步保護資料。有了互斥變數pthread_mutext_t為什麼還要引入條件變數pthread_cond_t呢? 原因就是防止CPU空轉,一個執行緒獲得互斥量之後,另外一個執行緒如果想獲取該互斥量,就會不斷的去查詢這個互斥量是否已經空閒可以被自己佔用,於是浪費了CPU週期。引入條件變數pthread_cond_t之後,如果條件不滿足,執行緒進入睡眠狀態,不會浪費CPU週期。

  NPTL進行執行緒同步的一般結構如下:

thread 1:
    pthread_mutex_lock(&mutex);
    while (!condition)
        pthread_cond_wait(&cond, &mutex);
    /* 實際操作,修改condition為無效 */
    pthread_mutex_unlock(&mutex);

thread2:
    pthread_mutex_lock(&mutex);
    /* 實際操作,修改condition為有效 */
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

  標準程式碼結構是像上面這樣,針對上面的結構提幾個問題?

  1. 為什麼要將pthread_cond_wait 放在while(!condition)迴圈內呢,為什麼要有while(!condition)的存在呢?

    2. pthread_cond_t 為什麼要和 pthread_mutex_t 一起使用呢,使用pthread_cond_signal的執行緒不使用pthread_mutext 行不行? 

 

  在回答上面的問題之前先介紹一下最核心的pthread_cond_wait(&cond, &mutex)在不同情況下都會幹些什麼。

  1. 程式執行到pthread_cond_wait() 條件發生,程式碼繼續向下執行。

  2. 程式執行到pthread_cond_wait() 條件未發生,函式呼叫首先會釋放mutex(開啟鎖),並使當前執行緒進入睡眠狀態。

  3. 睡眠在pthread_cond_wait()上的執行緒被signal喚醒,pthread_cond_wait()首先去獲得鎖(嘗試重新獲得該mutex直到獲得)。

 

  pthread_cond_wait()的行為為下面的討論做一個鋪墊。現在來考慮回答上面的問題,我們可以從多執行緒亂序執行做為切入點,thread1 有可能比thread2 先執行,thread2 也有可能比thread1先執行。

  1.首先考慮,如果thread2先執行並且已經執行到 pthread_cond_signal() 但是thread1甚至都還沒有執行,更別說進入到pthread_cond_wait()狀態,這時候沒有 while(!condition) 會怎麼樣?

  顯然thread2已經傳送了singal了,但是沒有接收者,此時出現了丟訊號的情況,即如果沒有 while(!condition) 當thread1進入到pthread_cond_wait()的時候就會睡眠,喚醒訊號丟失的情況發生,在這種情況下如果有 while(!condition) 的存在則不會執行pthread_cond_wait() 直接執行下面的程式碼。

  那麼用 if(!condtion) 不是也可以解決上面的問題嗎? 不錯是可以解決上面的問題,但是會帶來新的問題。考慮這種情況:如果signal同時喚醒了多個wait在該條件上的執行緒(pthread_cond_broadcast 或者出現傳說中的Spurious wakeup),那使用if(!condtion) 就是不行的。 這是因為,各個多個被喚醒的執行緒肯定會有一個會先進入被這個mutex保護的臨界區(回憶上面介紹的pthread_cond_wait()函式線上程醒來之前會嘗試去持有鎖直到持有為止),Linux上規定是低優先順序的執行緒先獲得該mutex,然後進行了操作,並修改了condition變數,釋放了mutex,此時另一個正在睡眠中但同時也在嘗試獲取該mutex的執行緒被喚醒,然後直接就向下執行,此時就會導致多執行緒同步失敗。如果使用while(!condtion)再次進行檢查則不會出現同步失敗的問題。      

  2.第二個問題相對簡單,如果cond不和mutex一起使用,那麼任何可以訪問cond的執行緒都可能喚醒睡眠在某個mutex上的執行緒,所以需要mutex對cond的保護,以確保有資格的執行緒才能對某個執行緒進行喚醒操作。

  理解條件變數的關鍵還是需要理解pthread_cond_wait()都幹了些什麼!

   

NPTL 多執行緒同步  條件變數 互斥變數 Linux

相關文章