互斥鎖與條件變數詳解

2021-09-11 22:11:18 字數 3033 閱讀 9365

互斥量(mutex)從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成後釋放互斥量上的鎖。在互斥量進行加鎖以後,任何其它試圖再次對互斥量加鎖的執行緒將會阻塞直到當前執行緒釋放該互斥鎖。如果釋放互斥鎖時有多個執行緒阻塞,所有在該互斥鎖上的阻塞執行緒都會變成可執行狀態,第乙個變為可執行狀態的執行緒可以對互斥鎖加鎖,其它執行緒將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用。

1、建立和登出

條件變數和互斥鎖一樣,都有靜態動態兩種建立方式,靜態方式使用pthread_cond_initializer常量,如下:     

pthread_cond_t   cond=pthread_cond_initializer
動態方式呼叫pthread_cond_init()函式,api定義如下:     

int   pthread_cond_init(pthread_cond_t   *cond,   pthread_condattr_t   *cond_attr)
儘管posix標準中為條件變數定義了屬性,但在linuxthreads中沒有實現,因此cond_attr值通常為null,且被忽略。   

登出乙個條件變數需要呼叫pthread_cond_destroy(),只有在沒有執行緒在該條件變數上等待的時候才能登出這個條件變數,否則返回ebusy。因為linux實現的條件變數沒有分配什麼資源,所以登出動作只包括檢查是否有等待執行緒。api定義如下:

int   pthread_cond_destroy(pthread_cond_t   *cond)
2、等待和激發

int   pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)   

int   pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)

首先,舉個例子:在應用程式中有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)

thread4:

while(1)

else

}

在上面**中由於thread4並不知道什麼時候icount會大於等於100,所以就會一直在迴圈判斷,但是每次判斷都要加鎖、解鎖(即使本次並沒有修改icount)。這就帶來了問題一:cpu浪費嚴重。所以在**中新增了sleep(),這樣讓每次判斷都休眠一定時間。但這又帶來的問題二:如果sleep()的時間比較長,導致thread4處理不夠及時,等icount到了很大的值時才重置。對於上面的兩個問題,可以使用條件變數來解決。

首先看一下使用條件變數後,執行緒**大概的樣子:

thread1/2:

while(1)

}

thread4:

while (1)

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上有個關於條件變數的文章: ,也可以看看。

補充:1、

問:條件變數為什麼要與互斥鎖一起使用呢?

答:這是為了應對執行緒4在呼叫pthread_cond_wait()但執行緒4還沒有進入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仍然要阻塞。

參考:

互斥鎖與條件變數

最近複習湯小丹的 計算機作業系統 西安電子科技大學出版社,第三版 程序 執行緒同步章節時,發現乙個疑問。在講程序同步時,提到了兩類方法 訊號量機制和管程機制。訊號量機制又包括四種 整型訊號量 記錄型訊號量 and型訊號量 訊號量集。如果採用整型訊號量或記錄型訊號量,則在共享多個資源時,可能出現程序死...

互斥鎖與條件變數

互斥鎖用於保護臨界區,使得任何時刻只有乙個執行緒在執行其中的 確切的說,互斥鎖用於保護多個執行緒或多個程序分享的共享資料。posix互斥鎖被宣告為具有pthread mutex t資料型別的變數。若互斥鎖變數是靜態分配的,則初始化為 static pthread mutex t lock pthre...

互斥鎖與條件變數

pthread cond wait總和乙個互斥鎖結合使用。在呼叫pthread cond wait前要先獲取鎖。pthread cond wait函式執行時先自動釋放指定的鎖,然後等待條件變數的變化。在函式呼叫返回之前,自動將指定的互斥量重新鎖住。int pthread cond signal pt...