互斥量(互斥鎖)與條件變數

2021-07-23 19:42:08 字數 3480 閱讀 2794

使用pthread的互斥介面來保護資料,確保同一時間只有乙個執行緒訪問資料。互斥量從本質上來說是把鎖。

條件變數是執行緒可用的另一種同步機制。條件變數給多個執行緒提供了乙個會和的場所。

條件變數與互斥量一起使用時,允許執行緒以無競爭的方式等待特定的條件發生。

條件本身是由互斥量保護的。我們使用pthread_cond_wait等待條件變數變為真。

pthread_cond_wait的內部過程:

呼叫者把鎖住的互斥量傳遞給函式,函式然後自動把呼叫執行緒放到等待條件的執行緒列表上對互斥量解鎖。這就關閉了條件檢查和執行緒進入休眠狀態等待條件改變這兩個操作之間的時間通道,這樣執行緒就不會錯過條件的任何變化。等待條件變數的變化(變為真或超時),pthread_cond_wait返回時,互斥量再次被鎖住。

從pthread_cond_wait呼叫成功返回時,執行緒需要重新計算條件,因為另乙個執行緒可能已經在執行並改變了條件。

下面兩個可以用於通知執行緒條件已經滿足:

int pthread_cond_signal(pthread_cond_t * cond);

pthread_cond_signal通過條件變數cond傳送訊息,若多個訊息在等待,它只喚醒乙個(存在多個等待執行緒時按入隊順序啟用其中乙個)。pthread_cond_broadcast可以喚醒所有。

呼叫pthread_cond_signal後要立刻釋放互斥鎖,因為pthread_cond_wait的最後一步是要將指定的互斥量重新鎖住,如果pthread_cond_signal之後沒有釋放互斥鎖,pthread_cond_wait仍然要阻塞。

一般情況下pthread_cond_wait必須放在pthread_mutex_lock和pthread_mutex_unlock之間(也有放後面的),因為他要根據共享變數的狀態來決定是否要等待,而為了不永遠等待下去所以必須要在lock/unlock隊中。

為什麼在喚醒執行緒後要重新mutex加鎖?

了解 pthread_cond_wait() 的作用非常重要 – 它是 posix 執行緒訊號傳送系統的核心,也是最難以理解的部分。

首先,讓我們考慮以下情況:執行緒為檢視已鏈結列表而鎖定了互斥物件,然而該列表恰巧是空的。這一特定執行緒什麼也幹不了 – 其設計意圖是從列表中除去節點,但是現在卻沒有節點。因此,它只能:鎖定互斥物件時,執行緒將呼叫 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 呼叫相當複雜,因此我們每次只執行它的乙個操作。

pthread_cond_wait() 所做的第一件事就是同時對互斥物件解鎖(於是其它執行緒可以修改已鏈結列表),並等待條件 mycond 發生(這樣當 pthread_cond_wait() 接收到另乙個執行緒的「訊號」時,它將甦醒)。現在互斥物件已被解鎖,其它執行緒可以訪問和修改已鏈結列表,可能還會新增項。 【要求解鎖並阻塞是乙個原子操作】

此時,pthread_cond_wait() 呼叫還未返回。對互斥物件解鎖會立即發生,但等待條件 mycond 通常是乙個阻塞操作,這意味著執行緒將睡眠,在它甦醒之前不會消耗 cpu 週期。這正是我們期待發生的情況。執行緒將一直睡眠,直到特定條件發生,在這期間不會發生任何浪費 cpu 時間的繁忙查詢。從執行緒的角度來看,它只是在等待 pthread_cond_wait() 呼叫返回。

現在繼續說明,假設另乙個執行緒(稱作「2 號執行緒」)鎖定了 mymutex 並對已鏈結列表新增了一項。在對互斥物件解鎖之後,2 號執行緒會立即呼叫函式 pthread_cond_broadcast(&mycond)。此操作之後,2 號執行緒將使所有等待 mycond 條件變數的執行緒立即甦醒。這意味著第乙個執行緒(仍處於 pthread_cond_wait() 呼叫中)現在將甦醒。

現在,看一下第乙個執行緒發生了什麼。您可能會認為在 2 號執行緒呼叫 pthread_cond_broadcast(&mymutex) 之後,1 號執行緒的 pthread_cond_wait() 會立即返回。不是那樣!實際上,pthread_cond_wait() 將執行最後乙個操作:重新鎖定 mymutex。一旦 pthread_cond_wait() 鎖定了互斥物件,那麼它將返回並允許 1 號執行緒繼續執行。那時,它可以馬上檢查列表,檢視它所感興趣的更改。

in thread1:

pthread_mutex_lock(&m_mutex);

pthread_cond_wait(&m_cond,&m_mutex);

pthread_mutex_unlock(&m_mutex);

in thread2:

pthread_mutex_lock(&m_mutex);

pthread_cond_signal(&m_cond);

pthread_mutex_unlock(&m_mutex);

為什麼要與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.

pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之間,也可以放在pthread_mutex_lock和pthread_mutex_unlock之後,但是各有有缺點。

之間:

pthread_mutex_lock

******x

pthread_cond_signal

pthread_mutex_unlock

缺點:在某下執行緒的實現中,會造成等待執行緒從核心中喚醒(由於cond_signal)然後又回到核心空間(因為cond_wait返回後會有原子加鎖的 行為),所以一來一回會有效能的問題。但是在linuxthreads或者nptl裡面,就不會有這個問題,因為在linux 執行緒中,有兩個佇列,分別是cond_wait佇列和mutex_lock佇列, cond_signal只是讓執行緒從cond_wait佇列移到mutex_lock佇列,而不用返回到使用者空間,不會有效能的損耗。

所以在linux中推薦使用這種模式。

之後:

pthread_mutex_lock

******x

pthread_mutex_unlock

pthread_cond_signal

優點:不會出現之前說的那個潛在的效能損耗,因為在signal之前就已經釋放鎖了

缺點:如果unlock和signal之前,有個低優先順序的執行緒正在mutex上等待的話,那麼這個低優先順序的執行緒就會搶占高優先順序的執行緒(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...