基於環形隊列的生產者消費者模型
上篇講解了生產者消費者模型以及基於BlockingQueue實現該模型,下篇這裏就實現一個基於環形隊列的生產者消費者模型吧!這個環形隊列用數組來實現。
1.如何把數組設置一個環形的隊列呢?
利用模運算來實現順序結構的環形操作。0 ~ 9模10依然是0 ~ 9,10模10就變成0了,也就回到了數組的頭部,從而實現一個環形隊列。
2.那麼如何判斷該環形隊列的空和滿呢?
剛開始的時候生產者和消費者指向同一塊空間,生產者在生產的時候,先判斷前面的格子裏是不是消費者,如果前面的格子裏不是消費者就在當前格子裏生產數據,然後++,如果前面的格子裏是消費者,那就不在當前的格子裏生產數據,保持不動。
假如現在有10個格子,下標爲0 ~ 9,消費者依然在0號格子,生產者生產到8號格子了,那麼生產者判斷9號格子不是消費者,於是在8號格子裏生產數據,然後++到9號格子,此時繼續判斷,生產者發現9號格子的下一個0號格子裏是消費者,所以此時生產者不再生產,表示該環形隊列已經滿了。所以我們採用浪費一個格子的操作實現了判空和判滿。
3.四條規則
- 消費者不能超過生產者(如果消費者快,但是生產者還沒生產完的時候,消費者就不能進行消費,消費者必須等生產者生產完)
- 生產者不能把消費者套一個圈(如果生產者快,而且隊列已經被生產者生產滿了,此時生產者就不能進行生產了,生產者此時就必須等消費者消費完才能繼續生產)
- 環形隊列爲空的時候,生產者先出發
- 環形隊列爲滿的時候,消費者先出發
4.怎麼實現?
引入兩個信號量,一個記錄格子資源個數,另一個記錄數據資源個數。
生產者生產的時候要申請空格子(P格子:格子資源數減一),那麼生產完畢之後有數據的格子就會多一個(V數據:數據資源數加一)。
消費者消費的時候就是要拿取有數據的格子(P數據:數據資源數減一),那麼消費完畢之後空格子就會多一個(V格子:格子資源數加一)
具體實現代碼解釋:
運行結果:
此時生產者生產的快,因此會迅速把環形隊列生產滿,滿了之後基於信號量的同步和互斥它就不能生產了,因此消費者開始每隔一秒消費一條數據,當消費者消費完一條數據之後,格子信號量就會增加1,因此生產者又會快馬加鞭的生產一條數據,消費者再隔一秒消費一條數據,生產者還是窮追不捨的又生產一條數據,因此看到的結果是生產者迅速生產滿之後,就變成了消費一條生產一條。
代碼如下:
1 #include <iostream>
2 #include <pthread.h>
3 #include <stdlib.h>
4 #include <time.h>
5 #include <semaphore.h>
6 #include <unistd.h>
7 #define NUM 16
8 class ring{
9 private:
10 int Ring[NUM];
11 sem_t blank_sem;
12 sem_t data_sem;
13 int p_step;
14 int c_step;
15 public:
16 void P(sem_t *sem)
17 {
18 sem_wait(sem);
19 }
20 void V(sem_t *sem)
21 {
22 sem_post(sem);
23 }
24 public:
25 ring()
26 :p_step(0),c_step(0)
27 {
28 sem_init(&blank_sem,0,NUM);
29 sem_init(&data_sem,0,0);
30 }
31 void PushData(const int &data)
32 {
33 P(&blank_sem);
34 Ring[p_step]=data;
35 p_step++;
36 p_step%=NUM;
37 V(&data_sem);
38 }
39 void PopData(int &data)
40 {
41 P(&data_sem);
42 data=Ring[c_step];
43 c_step++;
44 c_step%=NUM;
45 V(&blank_sem);
46 }
47 ~ring()
48 {
49 sem_destroy(&blank_sem);
50 sem_destroy(&data_sem);
51 }
52 };
53 void *product(void *arg)
54 {
55 ring *r=(ring *)arg;
56 srand((unsigned long)time(NULL));
57 while(1)
58 {
59 int data=rand()%100+1;
60 r->PushData(data);
61 std::cout<<"product done:"<<data<<std::endl;
62 }
63 }
64 void *consume(void *arg)
65 {
66 ring *r=(ring *)arg;
67 while(1)
68 {
69 int data;
70 r->PopData(data);
71 std::cout<<"consume done:"<<data<<std::endl;
72 sleep(1);
73 }
74 }
75 int main()
76 {
77 ring r;
78 pthread_t p,c;
79 pthread_create(&p,NULL,product,(void *)&r);
80 pthread_create(&c,NULL,consume,(void *)&r);
81 pthread_join(p,NULL);
82 pthread_join(c,NULL);
83 return 0;
84 }