執行緒同步 條件變數和訊號量

2021-07-29 11:17:28 字數 3163 閱讀 7400

上一節提到了執行緒互斥和同步的概念,並且給出了兩種用於解決共享資源互斥的利器:互斥鎖和讀寫鎖。那麼本節將介紹兩種用於解決執行緒同步的概念:條件變數和訊號量。

一. 條件變數

1.基本概念

互斥鎖的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變數通過允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法彌補了互斥鎖的不足。條件變數的內部實質上是乙個等待佇列,放置等待(阻塞)的執行緒,執行緒在條件變數上等待和通知,互斥鎖用來保護等待佇列(因為所有的執行緒都可以放入等待佇列,所以等待佇列成為了乙個共享的資源,需要被上鎖保護),因此條件變數通常和互斥鎖一起使用。

條件變數允許執行緒等待特定條件(判斷條件一般由使用者自己給出)發生,當條件不滿足時,執行緒通常先進入阻塞狀態,等待條件發生變化。一旦其他的某個執行緒改變了條件,就可以喚醒等待佇列中的乙個或多個阻塞的執行緒。

2.相關定義

資料型別:pthread_cond_t;

初始化:pthread_cond_init();

銷毀:pthread_cond_destroy();

條件變數等待操作:pthread_cond_wait();

帶有超時的等待:pthread_cond_timewait();

條件變數的解除等待操作:pthread_cond_signal();——通知單個執行緒

條件變數的解除等待操作:pthread_cond_broadcast();——通知所有執行緒

下面給出乙個簡單的案例來說明條件變數的用法與作用。

該案例主要實現:乙個執行緒用於計算某一結果,另乙個執行緒用於獲取該計算結果,當計算執行緒完成計算前,獲取結果的執行緒需要處於等待的狀態。

#include #include #include #include /*

* 完成乙個執行緒負責計算結果,乙個執行緒負責獲取結果

* 用到條件變數

*/typedef struct

result;//共享資源

// 計算並將結果放置於result中的執行緒執行函式

void *set_fn(void *arg)

//跳出迴圈,說明對方準備好

pthread_mutex_unlock(&r->mutex);

//通知喚醒等待的那個獲取結果的執行緒

pthread_cond_broadcast(&r->cond);

return (void*)0;

}// 獲取結果的執行緒執行函式

void *get_fn(void *arg)

int main(void)

//啟動獲取結果的執行緒

if((err = pthread_create(&cal, null,

set_fn, (void*)&r)) != 0)

// 主線程阻塞一下

pthread_join(cal, null);

pthread_join(get, null);

pthread_cond_destroy(&r.cond);

pthread_mutex_destroy(&r.mutex);

return 0;

}

說明:在get_fn函式中,解鎖的語句pthread_mutex_unlock()要放在等待語句pthread_cond_wait()之後,原因是在wait函式內部實現中是先對之前的鎖進行解鎖,然後將自己放入等待對列中,然後再加鎖解鎖。

二. 訊號量

:在陳碩的《linux多執行緒伺服器端程式設計》這本書中,作者對於訊號量表達了這樣的觀點:我沒有遇到過需要使用訊號量的情況,無從談及個人經驗。我認為訊號量不是必備的同步原語,因為條件變數配合互斥器可以完全替代其功能,而且更不易用錯。另外,訊號量的另乙個問題在於它有自己的技術支援,而通常我們自己的資料結構也有長度值,這就造成了同樣的資訊存了兩份,需要保持一致,這增加了程式設計師的負擔和出錯的可能。

是的,確實像作者所說,條件變數和互斥器的配合使用可以解決訊號量所能解決的問題,但是我們可能不像作者那樣有著豐富的閱歷,這裡僅將訊號量作為一種解決問題的辦法講出來。

1.基本概念

訊號量本質上是乙個非負整數計數器,代表共享資源的數目,通常是用來控制對共享資源的訪問。

訊號量可以實現執行緒的同步和互斥。

通常sem_post()和sem_wait()函式對訊號量進行加減操作從而解決執行緒的同步和互斥。

2. 相關定義

資料型別:sem_t

訊號量的建立:sem_init()

訊號量的銷毀:sem_destroy(sem_t* sem, int pshared, unsigned value);

第乙個引數是訊號量指標,第二個引數表示是否在程序間共享的標誌,第三個引數表示訊號量的初值;

訊號量的加操作:sem_post();(呼叫一次加1)

訊號量的減操作:sem_wait();阻塞版本(呼叫一次減1)

訊號量的減操作:sem_trywait();非阻塞版本

當執行緒呼叫sem_wait()後,若訊號量的值小於0則執行緒阻塞。只有其他執行緒在呼叫sem_post對訊號量作加操作後並且其值大於等於0時,阻塞的執行緒才能繼續執行。

下面的案例是對上面計算獲取結果這個案例的改進,即利用訊號量來實現乙個程序計算結果,另乙個程序獲取結果。

#include #include #include #include #include /*

* 完成乙個執行緒負責計算結果,乙個執行緒負責獲取結果

*/typedef struct

result;//共享資源

// 計算並將結果放置於result中的執行緒執行函式

void *set_fn(void *arg)

// 獲取結果的執行緒執行函式

void *get_fn(void *arg)

int main(void)

//啟動獲取結果的執行緒

if((err = pthread_create(&get, null,

get_fn, (void*)&r)) != 0)

// 主線程阻塞一下

pthread_join(cal, null);

pthread_join(get, null);

sem_destroy(&r.sem);

return 0;

}

可以看出,對於本案例,相較於條件變數,使用訊號量更加的簡單直接。

多執行緒 互斥量 訊號量和條件變數

通常用於互斥訪問 pthread mutex t m mutex pthread mutex intit m mutex,null pthread mutex lock m mutex pthread mutex unlock m mutex pthread mutex destory m mute...

執行緒訊號量同步

thread sem.c include include include include define thread number 3 define repeat number 3 define delay time levels 10.0 sem t sem thread number void ...

執行緒同步 訊號量

執行緒同步方法 訊號量不常用,找到個帖子不錯,記錄一下!依賴的標頭檔案 include 函式宣告 sem t 表示訊號量 int sem init sem t sem,int pshared,unsigned int value 名稱 sem init 功能 initialize an unname...