互斥量和條件變量

ps:參考了不少博客,可是當時沒記下連接。。。linux

互斥器和條件變量用法以下:

pthread_mutex_lock(&lock);
while (condition_is_false) {
    pthread_cond_wait(&cond, &lock);
}

上面那個while能換成if嗎?答案是不能,不然會致使spurious wakeup虛假喚醒。由於不只要在pthread_cond_wait前要檢查條件是否成立,在pthread_cond_wait以後也要檢查。由於pthread_cond_wait不只能被pthread_cond_signal/pthread_cond_broadcast喚醒,並且還會被其它信號喚醒,後者就是虛假喚醒。
並且有可能多個線程被同時喚醒。那麼在第一個獲取完資源後,後面的全都沒法獲取資源了。
pthread_cond_wait內的互斥量只能保證同步。性能

linux的pthread_cond_wait是用futex系統調用,這個是慢速系統調用,看過apue知道任何慢速系統調用被信號打斷的時候會返回-1,而且把errno置爲EINTR,若是慢速系統調用的重啓功能被關閉,須要在調用該系統調用的地方手動重啓它,像下面這樣:線程

while (1) {
    int ret = syscall();
    if (ret < 0 && errno == EINTR)
        continue;
    else
        break;
}

可是futex不能這麼用,由於futex結束後到再次重啓這個過程有個時間窗,在這個窗口內可能發生了pthread_cond_signal/phread_cond_broadcast,若是發生這種狀況,再進行pthread_cond_wait的時候就錯過了一次條件變量的變化,就會無限等待下去。可是若是不像上面那樣寫又沒法重啓futex系統調用,咋整呢?這就回到了上面檢查布爾條件的時候爲何用while而不用if。code

用while不會由於虛假喚醒而錯過phread_cond_signal/pthread_cond_broadcast,並且在經過判斷while條件不成立檢測出這次喚醒爲虛假喚醒並繼續調用futex繼續等待。隊列


爲何要有條件變量:

舉個例子:在應用程序中有連個線程thread1,thread2,thread3和thread4,有一個int類型的全局變量iCount。iCount初始化爲0,thread1和thread2的功能是對iCount的加1,thread3的功能是對iCount的值減1,而thread4的功能是當iCount的值大於等於100時,打印提示信息並重置iCount=0。
hread4並不知道何時iCount會大於等於100,因此就會一直在循環判斷,可是每次判斷都要加鎖、解鎖(即便本次並無修改iCount)。這就帶來了問題一,CPU浪費嚴重。
因此經過條件變量能夠避免CPU的浪費,並及時通知。資源


釋放順序:

  • (1) 按照 unlock(mutex); condition_signal()順序, 當等待的線程被喚醒時,由於mutex已經解鎖,所以被喚醒的線程很容易就鎖住了mutex而後從conditon_wait()中返回了。
  • (2) 按照 condition_signal(); unlock(mutext)順序,當等待線程被喚醒時,它試圖鎖住mutex,可是若是此時mutex還未解鎖,則線程又進入睡眠,mutex成功解鎖後,此線程在再次被喚醒並鎖住mutex,從而從condition_wait()中返回。

能夠看到,按照(2)的順序,對等待線程可能會發生2次的上下文切換,嚴重影響性能。所以在後來的實現中,對(2)的狀況,若是線程被喚醒可是不能鎖住mutex,則線程被轉移(morphing)到互斥量mutex的等待隊列中,避免了上下文的切換形成的開銷。 -- wait morphing同步

相關文章
相關標籤/搜索