基于链表和环形队列的生产者消费者模型

生产者,消费者模型

在学习系统编程当中,有一种很重要的模型,那就是生产者消费者模型,在编写多线程代码时候,一个个线程就扮演着生产者,消费者的角色。举一个例子,在生活中,我们去商店买东西,那么我们相当于消费者,而货物生成厂家就是生产者,商店扮演着交易场所。我们总结为:

  • 三中关系,生产者与生产者,生产者与消费者,消费者与消费者。
  • 两种角色,生产者和消费者。
  • 一个交易场所

生产者和生产者
这些厂商在生产货物的时候,加入都生产一种货物,而一家商店只需要从一家厂商订购货物,那么这些厂商之间就会竞争这个名额,故而生产者生产者之间存在一种互斥的关系。
生产者消费者
商家的商品再生产完成之间,我们是不能够购买的,所以存在互斥关系,并且当货架上面的货物满的时候,厂家是不会去送货物的,只有等待消费者消费了货物之后,才会生产,而且,当货物被买完的时候,消费者必须要等厂商供货来,才可以消费,因此,生产者和消费者存在同步关系。
消费者和消费者
当货物的资源有限的时候,而需求量又很大,那么消费者之间就会竞争这个资源,就会存在互斥关系。


我们再把上面的例子实例到我们的操作系统中时,当我们编写几个线程同时往一快内存中写数据,另外几个线程就是从这块内存中读取数据,那么就是典型的生产者,消费者模型,几个写线程之间必须要互斥的访问这块内存,同样的,读线程也必须互斥的访问,读写线程互斥而且同步,如果无法保证这些条件,那么就会造成线程访问临界资源的错误。下面我们来基于链表实现一个生产者,消费者模型,

基于链表的生产者消费者模型

  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 }    

这里生产者在向链表中插入数据,消费者从链表中读取数据,我们通过互斥量和条件变量来控制
生产者消费者的三种关系。
来看看结果:
这里写图片描述

基于环形队列的生产者消费者模型

我们下面来利用环形队列的数据结构来实现生产者,消费者模型,那么先来看看环形队列这种数据结构,和几种约束规则。

  • 我们在学习队列这种数据结构的时候,通常都是基于顺序表,或者链表的存储结构,这中结构当出队列时,空间的利用率比较低,我们可以用一段连续的空间,模拟实现一个环形队列。
    这里写图片描述
  • 一开始,生产者和消费者同时指向同一个节点,当生产者产生一个节点时候,生产者就顺时针指向下一个节点,出队列时,消费者顺时针指向下一个节点,当生产者,消费者指向一起时,不是队列为空就是队列为满,在一般情况下,如果队列满了,队头可以覆盖下一个节点,而生产者消费者模型中,生产者阻塞。

生产者消费者访问队列规则:

  1. 一开始,队列中没有元素,必须要让生产者先运行,消费者跟着生产者,并且消费者不能超过生产者。
  2. 生产者不能超过消费者一圈,当生产者跟消费者相遇,即队列满的时候,生产者必须等待消费者消费完数据,再生产。
    我们发现,环形的队列需要一个类似于计数器的东西来记录队列中的节点个数,和空白节点,所以很容易就会想到信号量的概念,通过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 }                                                                                                                                                                                                            

看一下结果:
这里写图片描述

发布了46 篇原创文章 · 获赞 16 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章