生產者消費者模型(Linux系統下的兩種實現方法)

2021-07-22 13:44:48 字數 3424 閱讀 5835

生產者消費者問題是同步問題中的一種常見情況,借用一下維基百科的話

生產者消費者問題(英語:producer-consumer problem),也稱有限緩衝問題(英語:bounded-buffer problem),是乙個多執行緒

同步問題的經典案例。該問題描述了兩個共享固定大小緩衝區的執行緒——即所謂的「生產者」和「消費者」——在實際執行時會發生的問題。生產者的主要作用是生成一定量的資料放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些資料。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入資料,消費者也不會在緩衝區中空時消耗資料。

訊號量:

訊號量的特性如下:訊號量是乙個非負整數(車位數),所有通過它的執行緒/程序(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的執行緒都將處於等待狀態。在訊號量上我們定義兩種操作: wait(等待) 和 release(釋放)。當乙個執行緒呼叫wait操作時,它要麼得到資源然後將訊號量減一,要麼一直等下去(指放入阻塞佇列),直到訊號量大於等於一時。release(釋放)實際上是在訊號量上執行加操作,對應於車輛離開停車場,該操作之所以叫做「釋放」是因為釋放了由訊號量守護的資源。

wait, release在linux下

int sem_wait(sem_t * sem);

int sem_post(sem_t * sem);

設定兩個訊號量,empty用來表示空槽的個數,full用來表示占有的個數

生產者在向任務佇列裡放資源時,呼叫sem_wait(&full)來檢查佇列是否已滿,如果滿的話,就阻塞,直到有消費者從裡面取資源再甦醒,如果不滿,就放資源,並通知消費者來取。

消費者在從任務佇列裡取資源時,呼叫sem_wait(&empty)來檢查佇列是否為空,如果空的話,就阻塞,直到有生產者向裡面放資源再甦醒,如果不空,就取資源,並通知生產者來放。

而互斥鎖僅僅是為了防止多個執行緒同時對佇列進行操作,造成未知的結果。

[cpp]view plain

copy

#include 

#include 

#include 

#define max 5  //佇列長度

pthread_mutex_t mutex = pthread_mutex_initializer;  

sem_t full;     //填充的個數

sem_t empty;    //空槽的個數

inttop = 0;     

//隊尾

intbottom = 0;  

//隊頭

void

* produce(

void

* arg)  

return

(void

*)1;  

}  void

* consume(

void

* arg)  

return

(void

*)2;  

}  int

main(

intargc, 

char

*argv)    

注:如果把sem_wait()和sem_post()放到pthread_mutex_lock()與pthread_mutex_unlock()之間會如何呢?

答案是:死鎖,因為我們不能預知執行緒進入共享區順序,如果消費者執行緒先對mutex加鎖,並進入,sem_wait()發現隊列為空,阻塞,而生產者在對mutex加鎖時,發現已上鎖也阻塞,雙方永遠無法喚醒對方。

條件變數的常見用法是在不滿足某些條件時,阻塞自己,直到有執行緒通知自己醒來。

而互斥量在這裡的作用依然還是防止多執行緒對共享資源同時操作,造成未知結果。

生產者消費者的行為與之前相同,只不過原來只呼叫sem_wait()可以完成兩步,1是檢查條件,2是阻塞,現在條件變數需要我們自己來設定條件(所以說條件變數配合互斥鎖比訊號量的功能更強大,因為它可以自定義休眠條件,但是這對使用者的要求也提高了,必須理清邏輯關係避免死鎖)

[cpp]view plain

copy

#include 

#include 

#define max 5

pthread_mutex_t mutex = pthread_mutex_initializer;  

pthread_cond_t notfull = pthread_cond_initializer;  //是否隊滿

pthread_cond_t notempty = pthread_cond_initializer;     //是否隊空

inttop = 0;  

intbottom = 0;  

void

* produce(

void

* arg)  

top = (top+1) % max;  

printf("now top is %d\n"

, top);  

pthread_cond_signal(¬empty);//發出隊非空的訊息

pthread_mutex_unlock(&mutex);  

}  return

(void

*)1;  

}  void

* consume(

void

* arg)  

bottom = (bottom+1) % max;  

printf("now bottom is %d\n"

, bottom);  

pthread_cond_signal(¬full);//發出隊不滿的訊息

pthread_mutex_unlock(&mutex);  

}  return

(void

*)2;  

}  int

main(

intargc, 

char

*argv)    

注:為什麼訊號量在互斥區外,而條件變數在互斥區內呢?

因為互斥鎖本質上是二元訊號量,和訊號量互斥的原理相同,而且放在互斥區會死鎖,而條件變數是和互斥鎖協同配合的,

我們從pthread_cond_wait()和pthread_cond_signal()的內部實現就可以看出

pthread_cond_wait()是先將互斥鎖解開,並陷入阻塞,直到pthread_signal()發出訊號後pthread_cond_wait()再加上鎖,然後退出,可以看到它們在設計時就是為了協同配合,而互斥鎖和訊號量都是由linux下的futex機制實現的,這裡就不展開說了

這裡貼出了pthread_wait()原始碼圖

Linux 生產者消費者模型

生產者消費者模型 為什麼要使用生產者消費者模型?生產者消費者模型遵循 基於生產者消費者模型的阻塞佇列 include include include include include include using namespace std class blockqueue void unlockque...

生產者消費者模型

1.生產者消費者問題 producer consumer 有限緩衝,多執行緒同步。生產者執行緒和消費者執行緒共享固定大小緩衝區。2.關鍵是保證生產者不會再緩衝區滿時加入資料,消費者不會在緩衝區空時消耗資料。3.解決辦法 讓生產者在緩衝區滿時休眠,等下次消費者消耗緩衝區中的資料的時候,生產者才能被喚醒...

生產者消費者模型

生產者與消費者 3,2,1 三種關係 生產者與消費者 互斥,同步 消費者與消費者 互斥 生產者與生產者 互斥 條件變數 int pthread cond destroy pthread cond t cond int pthread cond init pthread cond t restrict...