summary
Producer consumer model: a typical design pattern, which is a solution designed by people according to typical scenes
Application scenario: it is applied to the scenario where a large amount of data is generated and processed
Specific implementation: there are multiple producer and consumer threads to produce and process a huge amount of data, but producers and consumers do not interact directly, but coordinate through the middle buffer; The data generated by the producer is stored in the buffer, and the consumer gets the data from the buffer and processes it.
advantage:
Decoupling: separate production and consumption. When the data processing mode changes, only change the consumers
Support uneven busy and idle: if the amount of production data is too large, it can be stored in the buffer for consumers to process slowly
Support Concurrency: you can open multiple producer threads or consumer threads to work
In order to support concurrency, thread safety must be guaranteed:
1. Each producer should maintain a mutually exclusive relationship; Each consumer should also maintain a mutually exclusive relationship;
2. Producers and consumers should maintain a synchronous and mutually exclusive relationship
realization
Bottom implementation: producer thread and consumer thread (one in the queue and one out of the queue) + the queue to ensure thread safety is used as the buffer
1. Thread safe queue implementation
Encapsulate a thread safe queue class: class BlockQueue - blocking queue
If there is an idle node, data can be queued, and if there is no, the queued thread will be blocked;
If there is a data node, the data can be dequeued, and if there is no data node, the dequeued thread will be blocked;
Idea:
class BlockQueue{ private: int capacity; //capacity std::queue<int> _queue; //The bottom layer encapsulates a queue pthread_mutex_t mutex; //mutex pthread_cond_t cond_pro; //Producer condition variable pthread_cond_t cond_cus; //Consumer condition variable public: BlockQueue(int capacity=5); //Constructor bool push(int data); //Join the team bool pop(int* data); //Out of the team ~BlockQueue(); //Deconstruction };
2. Create a thread to queue and dequeue data
Full code:
#include<iostream> #include<queue> #include<pthread.h> #include<cstdio> #include<unistd.h> class BlockQueue{ private: int capacity; //capacity std::queue<int> _queue; //The bottom layer encapsulates a queue pthread_mutex_t mutex; //mutex pthread_cond_t cond_pro; //Producer condition variable pthread_cond_t cond_cus; //Consumer condition variable public: BlockQueue(int cap=5):capacity(cap){ pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond_pro,NULL); pthread_cond_init(&cond_cus,NULL); } bool Push(int data){ pthread_mutex_lock(&mutex); while(_queue.size()==capacity){ pthread_cond_wait(&cond_pro,&mutex); } _queue.push(data); pthread_cond_signal(&cond_cus); pthread_mutex_unlock(&mutex); return true; } bool Pop(int* data){ pthread_mutex_lock(&mutex); while(_queue.empty()){ pthread_cond_wait(&cond_cus,&mutex); } *data=_queue.front(); _queue.pop(); pthread_cond_signal(&cond_pro); pthread_mutex_unlock(&mutex); return true; } ~BlockQueue(){ pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond_pro); pthread_cond_destroy(&cond_cus); } }; void* productor(void* arg){ BlockQueue* q=(BlockQueue*)arg; int i=0; while(1){ q->Push(i); printf("Producer queue data:%d\n",i++); } return NULL; } void* customer(void* arg){ BlockQueue* q=(BlockQueue*)arg; while(1){ int data; q->Pop(&data); printf("Consumer out of line data:%d\n",data); } return NULL; } int main(){ BlockQueue q; pthread_t ptid[4],ctid[4]; int ret; for(int i=0;i<4;i++){ ret=pthread_create(&ptid[i],NULL,productor,&q); if(ret!=0){ printf("create thread error\n"); return -1; } } for(int i=0;i<4;i++){ ret=pthread_create(&ctid[i],NULL,customer,&q); if(ret!=0){ printf("create thread error\n"); return -1; } } for(int i=0;i<4;i++){ pthread_join(ptid[i],NULL); pthread_join(ctid[i],NULL); } return 0; }
Partial results:
be careful:
The capacity defined here is 5, so normally it should be 5 times in the queue and 5 times out of the queue alternately. However, since the operations in cus and pro threads are non atomic operations, the previous unfinished operations may be interrupted during the rotation of time slice.