生产者,消费者模型
在学习系统编程当中,有一种很重要的模型,那就是生产者消费者模型,在编写多线程代码时候,一个个线程就扮演着生产者,消费者的角色。举一个例子,在生活中,我们去商店买东西,那么我们相当于消费者,而货物生成厂家就是生产者,商店扮演着交易场所。我们总结为:
- 三中关系,生产者与生产者,生产者与消费者,消费者与消费者。
- 两种角色,生产者和消费者。
- 一个交易场所
生产者和生产者
这些厂商在生产货物的时候,加入都生产一种货物,而一家商店只需要从一家厂商订购货物,那么这些厂商之间就会竞争这个名额,故而生产者生产者之间存在一种互斥的关系。
生产者消费者
商家的商品再生产完成之间,我们是不能够购买的,所以存在互斥关系,并且当货架上面的货物满的时候,厂家是不会去送货物的,只有等待消费者消费了货物之后,才会生产,而且,当货物被买完的时候,消费者必须要等厂商供货来,才可以消费,因此,生产者和消费者存在同步关系。
消费者和消费者
当货物的资源有限的时候,而需求量又很大,那么消费者之间就会竞争这个资源,就会存在互斥关系。
我们再把上面的例子实例到我们的操作系统中时,当我们编写几个线程同时往一快内存中写数据,另外几个线程就是从这块内存中读取数据,那么就是典型的生产者,消费者模型,几个写线程之间必须要互斥的访问这块内存,同样的,读线程也必须互斥的访问,读写线程互斥而且同步,如果无法保证这些条件,那么就会造成线程访问临界资源的错误。下面我们来基于链表实现一个生产者,消费者模型,
基于链表的生产者消费者模型
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 }
看一下结果: