生產者,消費者模型
在學習系統編程當中,有一種很重要的模型,那就是生產者消費者模型,在編寫多線程代碼時候,一個個線程就扮演着生產者,消費者的角色。舉一個例子,在生活中,我們去商店買東西,那麼我們相當於消費者,而貨物生成廠家就是生產者,商店扮演着交易場所。我們總結爲:
- 三中關係,生產者與生產者,生產者與消費者,消費者與消費者。
- 兩種角色,生產者和消費者。
- 一個交易場所
生產者和生產者
這些廠商在生產貨物的時候,加入都生產一種貨物,而一家商店只需要從一家廠商訂購貨物,那麼這些廠商之間就會競爭這個名額,故而生產者生產者之間存在一種互斥的關係。
生產者消費者
商家的商品再生產完成之間,我們是不能夠購買的,所以存在互斥關係,並且當貨架上面的貨物滿的時候,廠家是不會去送貨物的,只有等待消費者消費了貨物之後,纔會生產,而且,當貨物被買完的時候,消費者必須要等廠商供貨來,纔可以消費,因此,生產者和消費者存在同步關係。
消費者和消費者
當貨物的資源有限的時候,而需求量又很大,那麼消費者之間就會競爭這個資源,就會存在互斥關係。
我們再把上面的例子實例到我們的操作系統中時,當我們編寫幾個線程同時往一快內存中寫數據,另外幾個線程就是從這塊內存中讀取數據,那麼就是典型的生產者,消費者模型,幾個寫線程之間必須要互斥的訪問這塊內存,同樣的,讀線程也必須互斥的訪問,讀寫線程互斥而且同步,如果無法保證這些條件,那麼就會造成線程訪問臨界資源的錯誤。下面我們來基於鏈表實現一個生產者,消費者模型,
基於鏈表的生產者消費者模型
1 #include<stdlib.h>
2 #include<time.h>
3 #include<stdio.h>
4 #include<pthread.h>
5 typedef struct node
6 {
7 int data;
8 struct node* next;
9 }node,*pnode,**ppnode;
10 void AllocNode(ppnode pphead)
11 {
12 *pphead=(pnode)malloc(sizeof(node));
13 if(*pphead==NULL)
14 {
15 perror("malloc");
16 exit(1);
17 }
18 (*pphead)->next=NULL;
19 return ;
20 }
21 void pushfront(pnode head,int x)
22 {
23 pnode p=(pnode)malloc(sizeof(node));
24 if(p==NULL)
25 {
26 perror("malloc");
27 return ;
28 }
29 p->next=NULL;
30 p->data=x;
31 p->next=head->next;
32 head->next=p;
33
34
35 }
36 void popfront(pnode head,int* x)
37 {
38 if(isempty(head))
39 exit(2);
40 if(x!=NULL)
41 *x=head->next->data;
42 pnode del=head->next;
43 head->next=del->next;
44 free(del);
45 del=NULL;
46 }
47 int isempty(pnode head)
48 {
49 return (head->next==NULL)?1:0;
50 }
51 void show(pnode head)
52 {
53 if(isempty(head))
54 return ;
55 while(head->next)
56 {
57 printf("%d ",head->next->data);
58 head=head->next;
59 }
60 printf("\n");
61 }
62 void destroylist(pnode head)
63 {
64 pnode h=head;
65 while(head->next)
66 {
67 popfront(head,NULL);
68 head=head->next;
69 }
70 free(h);
71 h=NULL;
72
73 }
74 pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
75 pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
76 void *runP(void *arg)
77 {
78 pnode head=*((ppnode)arg);
79 int i=0;
80 while(1)
81 {
82 sleep(1);
83 i=rand()%100+1;
84 pushfront(head,i);
85 printf("Protect is running ,data is:%d\n ",i);
86 pthread_cond_signal(&cond);
87 pthread_mutex_unlock(&mutex);
88 sleep(3);
89 }
90
91 }
92 void *runC(void*arg)
93 {pnode head=*((ppnode)arg);
94 int i=0;
95 while(1)
96 {
97 pthread_mutex_lock(&mutex);
98 while(head->next==NULL)
99 {
100 printf("Consumer need wait...\n");
101 pthread_cond_wait(&cond,&mutex);
102 }
103 popfront(head,&i);
104 printf("Custrom is running,data is:%d\n ",i);
105 sleep(1);
106 }
107
108 }
109 int main()
110 {
111 pnode head=NULL;
112 AllocNode(&head);
113 srand((unsigned long)time(NULL));
114 int i=0;
115 int x=0;
116 pthread_t t1,t2;
117 pthread_create(&t1,NULL,runP,&head);
118 pthread_create(&t2,NULL,runC,&head);
119 //for(;i<10;i++)
120 //{
121
122 //pushfront(head,i);
123 // show(head);
124 // sleep(1);
125 //}
126 //for(;i>5;i--)
127 //{
128 //
129 // popfront(head,&x);
130 // show(head);
131 // sleep(1);
132 //}
133 pthread_join(t1,NULL);
134 pthread_join(t2,NULL);
135 return 0;
136 }
這裏生產者在向鏈表中插入數據,消費者從鏈表中讀取數據,我們通過互斥量和條件變量來控制
生產者消費者的三種關係。
來看看結果:
基於環形隊列的生產者消費者模型
我們下面來利用環形隊列的數據結構來實現生產者,消費者模型,那麼先來看看環形隊列這種數據結構,和幾種約束規則。
- 我們在學習隊列這種數據結構的時候,通常都是基於順序表,或者鏈表的存儲結構,這中結構當出隊列時,空間的利用率比較低,我們可以用一段連續的空間,模擬實現一個環形隊列。
- 一開始,生產者和消費者同時指向同一個節點,當生產者產生一個節點時候,生產者就順時針指向下一個節點,出隊列時,消費者順時針指向下一個節點,當生產者,消費者指向一起時,不是隊列爲空就是隊列爲滿,在一般情況下,如果隊列滿了,隊頭可以覆蓋下一個節點,而生產者消費者模型中,生產者阻塞。
生產者消費者訪問隊列規則:
- 一開始,隊列中沒有元素,必須要讓生產者先運行,消費者跟着生產者,並且消費者不能超過生產者。
- 生產者不能超過消費者一圈,當生產者跟消費者相遇,即隊列滿的時候,生產者必須等待消費者消費完數據,再生產。
我們發現,環形的隊列需要一個類似於計數器的東西來記錄隊列中的節點個數,和空白節點,所以很容易就會想到信號量的概念,通過posix信號量,來進行線程的互斥和同步機制。
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<time.h>
4 #include<semaphore.h>
5 #include<stdlib.h>
6 #define M 10
7 #define P 3
8 #define C 3
9 int Deque[M];
10 pthread_mutex_t mutex1=PTHREAD_MUTEX_INITIALIZER;
11 pthread_mutex_t mutex2=PTHREAD_MUTEX_INITIALIZER;
12 sem_t num;
13 sem_t blank;
14 void* RunC(void *arg)
15 {
16 static int i=0;
17 while(1)
18 {
19 pthread_mutex_lock(&mutex2);
20 sem_wait(&num);
21 printf("Custrom is runing,data is:%d\n",Deque[i]);
22 i++;
23 i%M;
24 sem_post(&blank);
25
26 pthread_mutex_unlock(&mutex2);
27 sleep(2);
28 }
29
30 }
31 void*RunP(void *arg)
32 {
33
34 int d=0;
35 static int i=0;
36 while(1)
37 {
38 pthread_mutex_lock(&mutex1);
39 d=rand()%100+1;
40 sem_wait(&blank);
41 Deque[i]=d;
42 printf("Proteter is running, data is:%d\n",d);
43 i++;
44 i%M;
45 sem_post(&num);
46 pthread_mutex_unlock(&mutex1);
47 sleep(1);
48 }
49
50 }
51 int main()
52 {
53 srand((unsigned long)time(NULL));
54 int i=0;
55 sem_init(&num,0,0);
56 sem_init(&blank,0,M);
57 pthread_t Protect[P];
58 pthread_t Custrom[C];
59 for(;i<P;++i)
60 {
61 pthread_create(&Protect[i],NULL,RunP,NULL);
62 }
63 for(i=0;i<C;i++)
64 {
65 pthread_create(Custrom+i,NULL,RunC,NULL);
66 }
67 for(i=0;i<P;i++)
68 {
69 pthread_join(Protect[i],NULL);
70 }
71
72 for(i=0;i<C;i++)
73 {
74 pthread_join(Custrom[i],NULL);
75 }
76 sem_destroy(&num);
77 sem_destroy(&blank);
78 pthread_mutex_destroy(&mutex1);
79 pthread_mutex_destroy(&mutex2);
80 return 0;
81
82 }
看一下結果: