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