互斥量和條件變數

2021-07-27 23:00:40 字數 4565 閱讀 2311

一。互斥量和條件變數簡介

互斥量(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)

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總和乙個互斥鎖結合使用。在呼叫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仍然要阻塞。

下面是另一處說明:給出了函式執行全過程。 為什麼在喚醒執行緒後要重新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的執行緒),而這在上面的放中間的模式下是不會出現的。

互斥量,條件變數

在程序喚醒與睡眠一文中,針對多程序的生產者 消費者問題,我們提出了基於訊號量的解決方案 該方案避免了程序在等待其要求的下一步執行條件時進入忙等待狀態 我們使用了三個訊號量,訊號量mutex用於保證生產者程序與消費者程序不會同時訪問緩衝區 訊號量empty用於保證當緩衝區滿是生產者被阻塞進入休眠狀態,...

C 封裝互斥量和條件變數

1 互斥量是保護臨界區的另一種方法,當執行執行緒在臨界區的執行時間很長時,那麼就最好使用互斥量了,否則會造成其他的執行緒將會在臨界區外忙等,浪費cpu時間 此時其他執行緒發現臨界區已經被互斥量鎖住,那麼它們將會阻塞 當互斥量被釋放時,有多個執行緒在阻塞,多個執行緒均會被喚醒,但是只有乙個執行緒可以獲...

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

使用pthread的互斥介面來保護資料,確保同一時間只有乙個執行緒訪問資料。互斥量從本質上來說是把鎖。條件變數是執行緒可用的另一種同步機制。條件變數給多個執行緒提供了乙個會和的場所。條件變數與互斥量一起使用時,允許執行緒以無競爭的方式等待特定的條件發生。條件本身是由互斥量保護的。我們使用pthrea...