生產者-消費者模型的兩種實現方式

https://www.cnblogs.com/caolicangzhu/p/7086176.html

本文主要來總結生產者-消費者模型的代碼實現,至于其原理,請大家自行百度.

一、基于鏈表的生產-消費模型(條件變量)

  我們以鏈表為例,生產者進行頭部插入,消費者進行頭部刪除,因此,先將鏈表相關操作封裝為LinkList.h,具體代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//文件說明:LinkList.h
//作者:高小調
//創建時間:2017年06月27日 星期二 14時57分27秒
//開發環境:Kali Linux/g++ v6.3.0
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
//鏈表節點
typedef?struct?LinkListNode{
????int?val;
????struct?inkListNode *next;
}Node,*pNode,**ppNode;
//初始化鏈表
void?InitLinkList(ppNode head){
????assert(head);
????*head = NULL;
}
//判斷鏈表是否為空
int?IsEmpty(pNode head){
????return?head==NULL;
}
//申請新節點
pNode BuyNewNode(int?val){
????pNode ret = (pNode)malloc(sizeof(Node));
????ret->val = val;
????ret->next = NULL;
????return?ret;
}
//頭插
void?PushFront(ppNode head,int?val){
????assert(head);
????if(*head==NULL){
????????*head = BuyNewNode(val);
????????return?;
????}
????pNode newNode = BuyNewNode(val);
????newNode->next = *head;
????*head = newNode;
}
//頭刪
void?PopFront(ppNode head,int?*val){
????assert(head);
????if((*head) == NULL){
????????return?;
????}
????if((*head)->next == NULL){
????????*val = (*head)->val;
????????free(*head);
????????*head = NULL;
????????return?;
????}
????pNode del = *head;
????*head = del->next;
????*val = del->val;
????free(del);
}
//銷毀鏈表
void?Destory(ppNode head){
????assert(head);
????pNode cur = *head;
????pNode del = NULL;
????while(cur!=NULL){
????????del = cur;
????????cur = cur->next;
????????free(del);
????}
????*head = NULL;
}
//打印鏈表
void?PrintLinkList(pNode head){
????pNode cur = head;
????while(cur!=NULL){
????????printf("%d ",cur->val);
????????cur = cur->next;
????}
????printf("\n");
}

?  然后進入我們線程的生產消費模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//文件說明:test.c
//作者:高小調
//創建時間:2017年06月27日 星期二 14時56分13秒
//開發環境:Kali Linux/g++ v6.3.0
#include<stdio.h>
#include<pthread.h>
#include"LinkList.h"
//互斥鎖
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
//條件變量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//測試鏈表
void?TestLinkList(){
????pNode head;
????InitLinkList(&head);
????int?tmp;
????for(int?i=0; i<10; ++i){
????????PushFront(&head,i);
????????PrintLinkList(head);
????}
????for(int?i=0; i<10; ++i){
????????PopFront(&head,&tmp);
????????PrintLinkList(head);
????}
}
pNode head;
//生產者:每次向頭節點插入數據
void?*Productor(void*arg){
????int?val = 0;
????while(1){
????????//互斥鎖加鎖:確保生產時不會消費,消費時不會生產
????????pthread_mutex_lock(&lock);
????????val =?rand()%100;
????????PushFront(&head,val);
????????printf("Productor push %d\n",val);
????????//互斥鎖解鎖
????????pthread_mutex_unlock(&lock);
????????//條件變量,生產完成之后向消費者發出信號消費
????????pthread_cond_signal(&cond);
????????sleep(1);
????}
}
//消費者:每次將頭節點數據取出
void?*Consumer(void*arg){
????int?val = 0;
????while(1){
????????//互斥鎖
????????pthread_mutex_lock(&lock);
????????while(head==NULL){
????????????//鏈表中沒數據,阻塞等待生產者發消費信號
????????????printf("wait for data\n");
????????????pthread_cond_wait(&cond,&lock);
????????}
????????PopFront(&head,&val);
????????printf("Consumer pop %d\n",val);
????????pthread_mutex_unlock(&lock);
????????sleep(1);
????}
}
int?main(){
????InitLinkList(&head);
????pthread_t cid1,cid2;
????pthread_create(&cid1,NULL,Productor,NULL);
????pthread_create(&cid2,NULL,Consumer,NULL);
????pthread_join(&cid1,NULL);
????pthread_join(&cid2,NULL);
?????
????return?0;
}

?二、基于環形隊列的生產-消費模型(信號量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//文件說明:test2.c
//作者:高小調
//創建時間:2017年06月27日 星期二 16時29分30秒
//開發環境:Kali Linux/g++ v6.3.0
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<stdlib.h>
#define SIZE 1024
//環形隊列
int?arr[SIZE] = {0};
sem_t sem_pro;??????//描述環形隊列中的空位置
sem_t sem_con;??????//描述喚醒隊列中的數據
//生產者,只要環形隊列有空位,便不斷生產
void*productor(void*arg){
????int?data = 0;
????int?proIndex = 0;
????while(1){
????????//有空位便生產,沒空位便阻塞等消費者消費
????????sem_wait(&sem_pro);
????????data =?rand()%1234;
????????arr[proIndex] = data;
????????printf("product done %d\n",data);
????????proIndex = (proIndex+1)%SIZE;
????????//供消費者消費的數據加1
????????sem_post(&sem_con);
????}
}
//消費者,只要環形隊列中有數據,就不斷消費
void*consumer(void*arg){
????int?data = 0;
????int?conIndex = 0;
????while(1){
????????//環形隊列中存在數據則消費,不存在數據則阻塞,直到有數據為止
????????sem_wait(&sem_con);
????????data = arr[conIndex];
????????printf("consume done %d\n",data);
????????conIndex = (conIndex+1)%SIZE;
????????//最后,消費了一個數據,空位加1
????????sem_post(&sem_pro);
????}
}
int?main(){
????pthread_t pro,con;
????sem_init(&sem_pro,0,SIZE-1);????????//一開始有很多空位置
????sem_init(&sem_con,0,0);?????????//但并沒有數據
????pthread_create(&pro,NULL,productor,NULL);
????pthread_create(&con,NULL,consumer,NULL);
????pthread_join(pro,NULL);
????pthread_join(con,NULL);
????sem_destroy(&sem_pro);
????sem_destroy(&sem_con);
????return?0;
}

?


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383718.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383718.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383718.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux系統【一】CPU+MMU+fork函數創建進程

切板中的內容輸出到文件### 進程相關概念 程序&#xff1a;編譯好的二進制文件&#xff0c;在磁盤上&#xff0c;不占用系統資源&#xff08;不包括磁盤&#xff09;。&#xff08;劇本&#xff09; 進程&#xff1a;占用系統資源&#xff0c;是程序的一次運行。&#xff08;戲…

Ubuntu卸載軟件

用過使用dpkg軟件管理工具得到所有已經安裝的軟件&#xff0c;如果不清楚軟件的全名可以使用grep命令進行查找 然后再使用sudo apt-get remove --purge 軟件名卸載軟件&#xff08;--purge參數會刪除配置文件&#xff0c;刪的干凈一些&#xff09; 例如&#xff1a;

一個重要且實用的signal---SIGCHLD

https://blog.csdn.net/lyztyycode/article/details/78150805SIGCHLD(修改)因為筆者之前的文章里面有錯誤&#xff0c;今天發現&#xff0c;立馬做個修改。在下面我的一段關于sigchld信號相對于直接調用wait函數的好處時&#xff0c;我說調用wait函數要一直檢測子進程是否執行完…

數據結構實驗之鏈表七:單鏈表中重復元素的刪除

https://blog.csdn.net/blessingxry/article/details/794455111.知識點&#xff1a;逆序建立鏈表&#xff0b;節點刪除 2.題意&#xff1a;按照數據輸入的相反順序&#xff08;逆位序&#xff09;建立一個單鏈表&#xff0c;并將單鏈表中重復的元素刪除&#xff08;值相同的元素…

Python3函數和代碼復用

函數的定義 def 函數名([參數列表]):注釋函數體注意事項 函數形參不需要聲明類型&#xff0c;可以使用return語句在結束函數執行的同時返回任意類型的值&#xff0c;函數返回值類型與return語句返回表達式i的類型一致 即使該函數不需要接受任何參數&#xff0c;也必須保留一堆…

一文說盡C++賦值運算符重載函數(operator=)

http://www.cnblogs.com/zpcdbky/p/5027481.html在前面&#xff1a;關于C的賦值運算符重載函數(operator)&#xff0c;網絡以及各種教材上都有很多介紹&#xff0c;但可惜的是&#xff0c;內容大多雷同且不全面。面對這一局面&#xff0c;在下在整合各種資源及融入個人理解的基…

Python a和a[:]的區別

簡單來講a[:]是深復制&#xff0c;a是淺復制&#xff0c;相當于賦值a的話是賦值了指針&#xff0c;賦值a[:]相當于復制了a對應的那段空間 例如&#xff1a; a [1,1,1,1,1,1]for x in a:if x1:a.remove(x)print(a)運行結果&#xff1a; remove操作是移除序列中第一個x元素。…

約瑟夫環(c語言程序完整版)

https://blog.csdn.net/m_hahahaha1994/article/details/51742453約瑟夫環&#xff08;約瑟夫問題&#xff09;是一個數學的應用問題&#xff1a;已知n個人&#xff08;以編號1&#xff0c;2&#xff0c;3…n分別表示&#xff09;圍坐在一張圓桌周圍。從編號為k的人開始報數&am…

Linux系統【二】exec族函數及應用

文件描述符 文件描述符表是一個指針數組&#xff0c;文件描述符是一個整數。 文件描述符表對應的指針是一個結構體&#xff0c;名字為file_struct&#xff0c;里面保存的是已經打開文件的信息 需要注意的是父子進程之間讀時共享&#xff0c;寫時復制的原則是針對物理地址而言…

白話C++系列(27) -- RTTI:運行時類型識別

http://www.cnblogs.com/kkdd-2013/p/5601783.htmlRTTI—運行時類型識別 RTTI&#xff1a;Run-Time Type Identification。 那么RTTI如何來體現呢&#xff1f;這就要涉及到typeid和dynamic_cast這兩個知識點了。為了更好的去理解&#xff0c;那么我們就通過一個例子來說明。這個…

使用頭文件的原因和規范

原因 通過頭文件來調用庫功能。在很多場合&#xff0c;源代碼不便&#xff08;或不準&#xff09;向用戶公布&#xff0c;只 要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調用庫 功能&#xff0c;而不必關心接口怎么實現的。編譯器會從庫中提取相應…

轉圈踢人問題

https://www.cnblogs.com/lanxuezaipiao/p/3339603.html 有N個人圍一圈依次報數&#xff0c;數到3的倍數的人出列&#xff0c;問當只剩一個人時他原來的位子在哪里&#xff1f; 解答&#xff1a;經典的轉圈踢人問題&#xff0c;好吧專業一點&#xff0c;約瑟夫環問題&#xff0…

Linux系統【三】回收子進程

孤兒進程 父進程先于子進程結束&#xff0c;則子進程成為孤兒進程&#xff0c;子進程的父進程成為init進程&#xff0c;則稱init進程領養孤兒進程。現在好像是用戶進程中的system進程。 僵尸進程 進程終止&#xff0c;父進程不進行回收&#xff0c;自己成殘留資源(PCB)存放在…

string類的基本實現

https://blog.csdn.net/qq_29503203/article/details/52265829在面試中面試官常常會讓你寫出string類的基本操作&#xff0c;比如&#xff1a;構造函數&#xff0c;析構函數&#xff0c;拷貝構造等等.下面是除此之外的一些操作&#xff0c;希望可以幫助你更好的理解string以便以…

Python3常用數據結構

Python3中有三種組合數據類型&#xff0c;分別為&#xff1a; 序列類型&#xff1a;字符串&#xff08;str&#xff09;、元組&#xff08;tuple&#xff09;、列表&#xff08;list&#xff09;集合類型&#xff1a;集合&#xff08;set&#xff09;映射類型&#xff1a;字典…

Linux C++ 回射服務器

http://blog.csdn.net/qq_25425023/article/details/53914820回射服務器就是服務端將客戶端的數據發送回去。我實現的回射服務器返回增加了時間。服務端代碼&#xff0c;可以很容易看懂&#xff1a;[cpp] view plaincopy#include <sys/socket.h> #include <stdio.h&g…

TCP第四次揮手為什么要等待2MSL

當客戶端進入TIME-WAIT狀態的時候(也就是第四次揮手的時候)&#xff0c;必須經過時間計數器設置的時間2MSL(最長報文段壽命)后&#xff0c;才能進入關閉狀態&#xff0c;這時為什么呢&#xff1f;&#xff1f;&#xff1f; 這最主要是因為兩個理由&#xff1a; 1、為了保證客戶…

計算機網絡【一】概述+OSI參考模型

網絡概述 局域網:覆蓋范圍小(100m以內)&#xff0c;自己花錢買設備&#xff0c;帶寬固定(10M,100M,1000M)&#xff0c;自己維護&#xff08;接入層交換機直接連接電腦、匯聚層交換機直接連接接入層交換機&#xff09; 廣域網:距離遠&#xff0c;花錢買服務&#xff0c;租帶寬&…

單鏈表逆序的多種方式

https://www.cnblogs.com/eniac12/p/4860642.htmltemplate<class T> void List<T>::Inverse() {if(first NULL) return;LinkNode<T> *p, *prev, *latter; p first->link;   // 當前結點prev NULL;   // 前一結點l…

Linux系統【四】進程間通信-管道

進程間通信&#xff08;IPC Interprocess Communication&#xff09; 進程和進程之間的通信只能通過內核&#xff0c;在內核中提供一塊緩沖區進行通信。內核提供的這種機制叫做IPC 在進程間完成數據傳輸需要借助操作系統提供的特殊方法&#xff0c;如&#xff1a;文件&#xf…