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