一、互斥量和條件變量簡介
互斥量(mutex)從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成后釋放互斥量上的鎖。在互斥量進行加鎖以后,任何其它試圖再次對互斥量加鎖的線程將會阻塞直到當前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運行狀態,第一個變為可運行狀態的線程可以對互斥鎖加鎖,其它線程將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用。
條件變量(cond)是在多線程程序中用來實現“等待--》喚醒”邏輯的常用的方法。條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待“條件變量的條件成立”而掛起;另一個線程使“條件成立”。條件變量的使用總是和一個互斥鎖結合在一起。線程在改變條件狀態前必須首先鎖住互斥量,函數pthread_cond_wait把自己放到等待條件的線程列表上,然后對互斥鎖解鎖,這兩個操作是原子操作(所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何context switch(切換到另一個線程))。在函數返回時,互斥量再次被鎖住。
二、為什么存在條件變量
首先,舉個例子:在應用程序中有4個進程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在pthread_cond_wait()返回之后,顯然應該再檢查一遍iCount的大小,這就是while的用意,如果是if,則會直接往下執行,不會再次判斷。
感覺可以總結為:條件變量用於某個線程需要在某種條件成立時才去保護它將要操作的臨界區,這種情況從而避免了線程不斷輪詢檢查該條件是否成立而降低效率的情況,這是實現了效率提高。在條件滿足時,自動退出阻塞,再加鎖進行操作。
IBM上有個關於條件變量的文章:https://www.ibm.com/developerworks/cn/linux/thread/posix_thread3/ ,也可以看看。
2017-10-09 15:23:43補充:
1、
問:條件變量為什么要與pthread_mutex一起使用呢?
答:這是為了應對線程1在調用pthread_cond_wait()但線程1還沒有進入wait cond的狀態的時候,此時線程2調用了 cond_singal 的情況。 如果不用mutex鎖的話,這個cond_singal就丟失了。加了鎖的情況是,線程2必須等到 mutex 被釋放(也就是 pthread_cod_wait() 釋放鎖並進入wait_cond狀態 ,此時線程2上鎖) 的時候才能調用cond_singal
2、
調用pthread_cond_signal后要立刻釋放互斥鎖(也可以將pthread_cond_signal放在pthread_mutex_lock和pthread_mutex_unlock之后),因為pthread_cond_wait的最后一步是要將指定的互斥量重新鎖住,如果pthread_cond_signal之后沒有釋放互斥鎖,pthread_cond_wait仍然要阻塞。
轉自:http://blog.csdn.net/bolike/article/details/9025389
