為了解決這個問題,首先要非常深入了解每一個概念:
1. 互斥鎖(mutual exclusive lock variable / mutex )
互斥量(mutex)從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成后釋放互斥量上的鎖。對互斥量進行加鎖以后,任何其他試圖再次對互斥鎖加鎖的線程將會阻塞直到當前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運行狀態(tài),第一個變?yōu)檫\行狀態(tài)的線程可以對互斥鎖加鎖,其他線程將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變?yōu)榭捎谩?/strong>
2. 條件變量
條件變量(cond)是在多線程程序中用來實現(xiàn)"等待--》喚醒"邏輯常用的方法。條件變量利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使“條件成立”。為了防止競爭,條件變量的使用總是和一個互斥鎖結合在一起。線程在改變條件狀態(tài)前必須首先鎖住互斥量,函數(shù)pthread_cond_wait把自己放到等待條件的線程列表上,然后對互斥鎖解鎖(這兩個操作是原子操作)。在函數(shù)返回時,互斥量再次被鎖住。
那為什么有互斥鎖,還需要條件變量?
因為:互斥鎖和條件變量所解決的,是不同的問題,不同的場景。
互斥鎖解決的是在 shared memory space 模型下,多個線程對同一個全局變量的訪問的競爭問題。由于寫操作的非原子性(從內存中讀進寄存器,修改,如果其他線程完成了對這個變量的修改,則舊的修改就被覆蓋,等等問題),必須保證同一時間只有一個線程在進行寫操作。這就涉及到了互斥鎖,將臨界區(qū)的操作鎖起來,保證只有一個線程在進行操作。多個線程在等待同一把鎖的時候,按照 FIFO 組織隊列,當鎖被釋放時,隊頭線程獲得鎖(由操作系統(tǒng)管理,具體不表)。沒有獲得鎖的線程繼續(xù)被 block,換言之,它們是因為沒有獲得鎖而被 block。
假如我們沒有“條件變量”這個概念,如果一個線程要等待某個“自定義的條件”滿足而繼續(xù)執(zhí)行,而這個條件只能由另一個線程來滿足,比如 T1不斷給一個全局變量 x +1, T2檢測到x 大于100時,將x 置0,如果我們沒有條件變量,則只通過互斥鎖則可以有如下實現(xiàn):
/*
* Assume we have global variables:
* int iCount == 0;
* pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
*/
//thread 1:
while(true)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
}
//thread 2:
while(true)
{
pthread_mutex_lock(&mutex);
if(iCount >= 100)
{
iCount = 0;
}
pthread_mutex_unlock(&mutex);
}
這種實現(xiàn)下,就算 lock 空閑,thread2需要不斷重復<加鎖,判斷,解鎖>這個流程,會給系統(tǒng)帶來不必要的開銷。有沒有一種辦法讓 thread2先被 block,等條件滿足的時候再喚醒 thread2?這樣 thread2 就不用不斷進行重復的加解鎖操作了?這就要用到條件變量了:
//thread1 :
while(true)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
if(iCount >= 100)
{
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
}
//thread2:
while(1)
{
pthread_mutex_lock(&mutex);
while(iCount < 100)
{
pthread_cond_wait(&cond, &mutex);
}
printf("iCount >= 100\r\n");
iCount = 0;
pthread_mutex_unlock(&mutex);
}
需要注意的是,條件變量需要配合互斥鎖來使用:
為什么要與pthread_mutex 一起使用呢? 這是為了應對 線程1在調用pthread_cond_wait()但線程1還沒有進入wait cond的狀態(tài)的時候,此時線程2調用了 cond_singal 的情況。 如果不用mutex鎖的話,這個cond_singal就丟失了。加了鎖的情況是,線程2必須等到 mutex 被釋放(也就是 pthread_cod_wait() 釋放鎖并進入wait_cond狀態(tài) ,此時線程2上鎖) 的時候才能調用cond_singal.
簡而言之就是,在thread 1 call pthread_cond_wait() 的時刻到 thread 1真正進入 wait 狀態(tài)時,是存在著時間差的。如果在這段時間差內 thread2 調用了 pthread_cond_signal() 那這個 signal 信號就丟失了。給 wait 加鎖可以防止同時有另一個線程在 signal。
(好像還有其他原因,有空補。)