Linux學習筆記(05 25)條件變數的使用

2021-07-02 20:01:39 字數 4943 閱讀 7294

1.互斥量的存在問題:

互斥量是執行緒程式必需的工具,但它們並非萬能的。

如果執行緒正在等待共享資料內某個條件出現,它可以重複對互斥物件鎖定和解鎖,每次都會檢查共享資料結構,以查詢某個值。但這種繁忙查詢的效率非常低。

可以讓呼叫執行緒短暫地進入睡眠,比如睡眠三秒鐘,但就無法最快作出響應。

需要的是:當執行緒在等待滿足某些條件時使執行緒進入睡眠狀態。一旦條件滿足,就喚醒因等待滿足特定條件而睡眠的執行緒。如果能夠做到這一點,執行緒**將是非常高效的,並且不會占用寶貴的互斥物件鎖。

2.條件變數:

條件變數允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法,它常和互斥鎖一起使用。

使用時,條件變數被用來阻塞乙個執行緒,當條件不滿足時,執行緒往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個執行緒改變了條件變數,它將通知相應的條件變數喚醒乙個或多個正被此條件變數阻塞的執行緒。這些執行緒將重新鎖定互斥鎖並重新測試條件是否滿足。

3.條件變數的相關函式

(1)建立和登出

條件變數和互斥鎖一樣,都有靜態動態兩種建立方式

a.靜態方式

靜態方式使用pthread_cond_initializer常量,如下:

pthread_cond_t cond=pthread_cond_initializer

b.動態方式

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

使用 cond_attr 指定的屬性初始化條件變數 cond,當 cond_attr 為 null 時,使用預設的屬性。linuxthreads 實現條件變數不支援屬性,因此 cond_attr 引數實際被忽略。

c.登出

int pthread_cond_destroy(pthread_cond_t *cond)

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

(2)等待和激發

a.等待

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

pthread_cond_wait()是 posix 執行緒訊號傳送系統的核心。

執行緒為檢視已鏈結列表而鎖定了互斥物件,然而該列表恰巧是空的。

鎖定互斥物件時,執行緒將呼叫 pthread_cond_wait(&mycond,&mymutex)。

pthread_cond_wait() 所做的第一件事就是同時對互斥物件解鎖(於是其它執行緒可以修改已鏈結列表),並等待條件 mycond 發生(這樣當 pthread_cond_wait() 接收到另乙個執行緒的「訊號」時,它將甦醒)。

此時,pthread_cond_wait() 呼叫還未返回。對互斥物件解鎖會立即發生,但等待條件 mycond 通常是乙個阻塞操作,這意味著執行緒將睡眠,在它甦醒之前不會消耗 cpu 週期。這正是我們期待發生的情況。

從執行緒的角度來看,它只是在等待 pthread_cond_wait() 呼叫返回。

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

1 號執行緒的 pthread_cond_wait() 將執行最後乙個操作:重新鎖定 mymutex。一旦 pthread_cond_wait() 鎖定了互斥物件,那麼它將返回並允許 1 號執行緒繼續執行。那時,它可以馬上檢查列表,檢視它所感興趣的更改。

第乙個執行緒首先呼叫:

pthread_mutex_lock(&mymutex);

然後,它檢查了列表。沒有找到感興趣的東西,於是它呼叫:

pthread_cond_wait(&mycond, &mymutex);

然後,pthread_cond_wait() 呼叫在返回前執行許多操作:

pthread_mutex_unlock(&mymutex);

它對 mymutex 解鎖,然後進入睡眠狀態,等待 mycond 以接收 posix 執行緒「訊號」。「訊號」是來自 pthread_cond_signal() 或 pthread_cond_broadcast() 呼叫的訊號)。

pthread_cond_wait() 沒有立即返回, 重新鎖定 mutex:

pthread_mutex_lock(&mymutex);

pthread_cond_wait() 知道我們在查詢 mymutex 「背後」的變化,因此它繼續操作,為我們鎖定互斥物件,然後才返回。

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

pthread_cond_timedwait 和 pthread_cond_wait 一樣,自動解鎖互斥量及等待條件變數,但它還限定了等待時間。如果在 abstime 指定的時間內 cond 未觸發,互斥量 mutex 被重新加鎖,並返回錯誤 etimedout。abstime 引數指定乙個絕對時間,時間原點與 time 和 gettimeofday 相同:abstime = 0 表示 1970 年 1 月 1 日 00:00:00 gmt。

例如,要等待 5 秒,這樣處理:

struct timeval now;

struct timespec timeout;

gettimeofday(&now);

timeout.tv_sec = now.tv_sec + 5;

timeout.tv_nsec = now.tv_usec * 1000;

其中:           struct timeval ;

struct timespec ;

b.激發

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

激發條件有兩種形式,pthread_cond_signal()啟用乙個等待該條件的執行緒,多個執行緒阻塞在此條件變數上時,哪乙個執行緒被喚醒是由執行緒的排程策略所決定的;而pthread_cond_broadcast()則啟用所有等待執行緒,這些執行緒被喚醒後將再次競爭相應的互斥鎖。

要注意的是,必須用保護條件變數的互斥鎖來保護啟用函式,否則條件滿足訊號有可能在測試條件和呼叫pthread_cond_wait()函式之間被發出,從而造成無限制的等待。 例子

a、建立兩個執行緒1、2,兩個執行緒分別訪問共享資源,並進行加1操作,當共享資源<=3時,執行緒1掛起不操作,這時執行緒2工作,共享資源》3後,兩者都工作。

#include

#include

#include

#include

int gnum = 0;

pthread_mutex_t mutex;

pthread_cond_t  cond;

static void pthread_func_1 (void);

static void pthread_func_2 (void);

int main (void)

ret = pthread_create (&pt_2, null, (void *)pthread_func_2, null);

if (ret != 0)     

pthread_join (pt_1, null);

pthread_join (pt_2, null);

printf ("main programme exit!\n");

return 0;

}static void pthread_func_1 (void)     

//當執行緒2呼叫pthread_cond_signal(&cond)時,執行緒1在這裡重啟

//臨界資源

gnum++;

printf ("thread1 add one to num:%d\n",gnum);

pthread_mutex_unlock(&mutex);

}     

}static void pthread_func_2 (void)     

pthread_exit (0);     }b、

#include

#include

#include

pthread_mutex_t mutex = pthread_mutex_initializer;

pthread_cond_t cond = pthread_cond_initializer;

void *thread1(void *);

void *thread2(void *);

int i=1;

int main(void)

void *thread1(void *junk) }

void *thread2(void *junk) }

程式建立了2個新執行緒使他們同步執行,實現程序t_b列印10以內3的倍數,t_a列印其他的數,程式開始執行緒t_b不滿足條件等待,執行緒t_a執行使a迴圈加1並列印。直到i為3的倍數時,執行緒t_a傳送訊號通知程序t_b,這時t_b滿足條件,列印i值。

下面是執行結果:

#cc –lpthread –o cond cond.c

#./cond

thread1:1

thread1:2

thread2:3

thread1:4

thread1:5

thread2:6

thread1:7

thread1:8

thread2:9

Linux學習筆記25 條件變數

條件變數非常類似於js裡的訊號 執行緒a阻塞著等待條件變數變為真 監聽訊號 執行緒b發出通知已滿足條件 觸發訊號 執行緒a解除阻塞開始後面的操作 響應訊號 與之對應的,我們需要一些型別和介面來監聽 觸發訊號。條件變數的型別 pthread cond t 使用前需要進行初始化 int pthread ...

python學習筆記 7 條件判斷

根據python的縮排規則,如果if語句判斷是true,就把縮排的兩行print語句執行了,否則,什麼也不做。也可以給if新增乙個else語句,意思是,如果if判斷是false,不要執行if的內容,去把else執行了 if語句執行有個特點,它是從上往下判斷,如果在某個判斷上是true,把該判斷對應的...

react學習筆記 五 條件渲染

在react中,我們有時只想渲染部分元件,而在某些狀態發生改變時渲染另一部分元件,此時就要使用條件渲染 我們可以通過if else來判斷狀態值,從而渲染不同的元件 function usergreeting props function guestgreeting props function gr...