有了互斥量,為什麼還需要條件變數?
互斥量(mutex)從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成後釋放互斥量上的鎖。對互斥量進行加鎖以後,任何其他試圖再次對互斥鎖加鎖的執行緒將會阻塞直到當前執行緒釋放該互斥鎖。如果釋放互斥鎖時有多個執行緒阻塞,所有在該互斥鎖上的阻塞執行緒都會變成可執行狀態,第一個變為執行狀態的執行緒可以對互斥鎖加鎖,其他執行緒將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用。
條件變數(cond)是在多執行緒程式中用來實現"等待--》喚醒"邏輯常用的方法。條件變數利用執行緒間共享的全域性變數進行同步的一種機制,主要包括兩個動作:一個執行緒等待"條件變數的條件成立"而掛起;另一個執行緒使“條件成立”。為了防止競爭,條件變數的使用總是和一個互斥鎖結合在一起。執行緒在改變條件狀態前必須首先鎖住互斥量,函式pthread_cond_wait把自己放到等待條件的執行緒列表上,然後對互斥鎖解鎖(這兩個操作是原子操作)。在函式返回時,互斥量再次被鎖住。
二。為什麼存在條件變數
首先,舉個例子:在應用程式中有連個執行緒thread1,thread2,thread3和thread4,有一個int型別的全域性變數iCount。iCount初始化為0,thread1和thread2的功能是對iCount的加1,thread3的功能是對iCount的值減1,而thread4的功能是當iCount的值大於等於100時,列印提示資訊並重置iCount=0。
如果使用互斥量,執行緒程式碼大概應是下面的樣子:
thread1/2:
while (1)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
}
thread4:
while(1)
{
pthead_mutex_lock(&mutex);
if (100 <= iCount)
{
printf("iCount >= 100\r\n");
iCount = 0;
pthread_mutex_unlock(&mutex);
}
else
{
pthread_mutex_unlock(&mutex);
}
}
在上面程式碼中由於thread4並不知道什麼時候iCount會大於等於100,所以就會一直在迴圈判斷,但是每次判斷都要加鎖、解鎖(即使本次並沒有修改iCount)。這就帶來了問題一,CPU浪費嚴重。所以在程式碼中添加了sleep(),這樣讓每次判斷都休眠一定時間。但這由帶來的第二個問題,如果sleep()的時間比較長,導致thread4處理不夠及時,等iCount到了很大的值時才重置。對於上面的兩個問題,可以使用條件變數來解決。首先看一下使用條件變數後,執行緒程式碼大概的樣子:
thread1/2:
while(1)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
if (iCount >= 100)
{
pthread_cond_signal(&cond);
}
}
thread4:
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);
}
從上面的程式碼可以看出thread4中,當iCount < 100時,會呼叫pthread_cond_wait。而pthread_cond_wait在上面應經講到它會釋放mutex,然後等待條件變為真返回。當返回時會再次鎖住mutex。因為pthread_cond_wait會等待,從而不用一直的輪詢,減少CPU的浪費。在thread1和thread2中的函式pthread_cond_signal會喚醒等待cond的執行緒(即thread4),這樣當iCount一到大於等於100就會去喚醒thread4。從而不致出現iCount很大了,thread4才去處理。 需要注意的一點是在thread4中使用的while (iCount < 100),而不是if (iCount < 100)。這是因為在pthread_cond_singal()和pthread_cond_wait()返回之間有時間差,假如在時間差內,thread3又將iCount減到了100以下了,那麼thread4就需要在等待條件為真了。