基於鏈表和環形隊列的生產者消費者模型

生產者,消費者模型

在學習系統編程當中,有一種很重要的模型,那就是生產者消費者模型,在編寫多線程代碼時候,一個個線程就扮演着生產者,消費者的角色。舉一個例子,在生活中,我們去商店買東西,那麼我們相當於消費者,而貨物生成廠家就是生產者,商店扮演着交易場所。我們總結爲:

  • 三中關係,生產者與生產者,生產者與消費者,消費者與消費者。
  • 兩種角色,生產者和消費者。
  • 一個交易場所

生產者和生產者
這些廠商在生產貨物的時候,加入都生產一種貨物,而一家商店只需要從一家廠商訂購貨物,那麼這些廠商之間就會競爭這個名額,故而生產者生產者之間存在一種互斥的關係。
生產者消費者
商家的商品再生產完成之間,我們是不能夠購買的,所以存在互斥關係,並且當貨架上面的貨物滿的時候,廠家是不會去送貨物的,只有等待消費者消費了貨物之後,纔會生產,而且,當貨物被買完的時候,消費者必須要等廠商供貨來,纔可以消費,因此,生產者和消費者存在同步關係。
消費者和消費者
當貨物的資源有限的時候,而需求量又很大,那麼消費者之間就會競爭這個資源,就會存在互斥關係。


我們再把上面的例子實例到我們的操作系統中時,當我們編寫幾個線程同時往一快內存中寫數據,另外幾個線程就是從這塊內存中讀取數據,那麼就是典型的生產者,消費者模型,幾個寫線程之間必須要互斥的訪問這塊內存,同樣的,讀線程也必須互斥的訪問,讀寫線程互斥而且同步,如果無法保證這些條件,那麼就會造成線程訪問臨界資源的錯誤。下面我們來基於鏈表實現一個生產者,消費者模型,

基於鏈表的生產者消費者模型

  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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章