生产者消费者模型
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
生产者消费者模型优点:解耦、支持并发、支持忙闲不均
基于阻塞队列的生产者消费者模型
在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)
以单生产者和单消费者模拟
#include<iostream>
#include<queue>
#include<cstdlib>
#include<pthread.h>
#include<time.h>
#include<unistd.h>
#define MAX 10
using namespace std;
class BlockQueue
{
private:
queue<int> q;
size_t cap;
pthread_mutex_t lock;
pthread_cond_t full;
pthread_cond_t empty;
private:
void lockQueue()
{
pthread_mutex_lock(&lock);
}
void unlockQueue()
{
pthread_mutex_unlock(&lock);
}
void productWait()
{
pthread_cond_wait(&full, &lock);
}
void consumeWait()
{
pthread_cond_wait(&empty, &lock);
}
void productNotify()
{
pthread_cond_signal(&full);
}
void consumeNotify()
{
pthread_cond_signal(&empty);
}
bool isEmpty()
{
return ((q.size() == 0) ? true : false);
}
bool isFull()
{
return ((q.size() == cap) ? true : false);
}
public:
BlockQueue(int _cap = MAX):cap(_cap)
{
pthread_mutex_init(&lock, nullptr);
pthread_cond_init(&full, nullptr);
pthread_cond_init(&empty, nullptr);
}
~BlockQueue()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&full);
pthread_cond_destroy(&empty);
}
void pushData(const int& data)
{
lockQueue();
while(isFull())
{
consumeNotify();
cout<<"queue full, notify consume data, product stop..."<<endl;
productWait();
}
q.push(data);
consumeNotify();
unlockQueue();
}
void popData(int &data)
{
lockQueue();
while(isEmpty())
{
productNotify();
cout<<"queue empty, notify product data, consume stop..."<<endl;
consumeWait();
}
data = q.front();
q.pop();
productNotify();
unlockQueue();
}
};
void * Consumer(void * arg)
{
BlockQueue * pbq = static_cast<BlockQueue *>(arg);
int data;
while(true)
{
pbq->popData(data);
cout<<"consume data done: "<<data<<endl;
}
return nullptr;
}
void * Producter(void * arg)
{
BlockQueue * pbq = static_cast<BlockQueue *>(arg);
srand((unsigned int)time(nullptr));
while(true)
{
int data = rand() % 1024;
pbq->pushData(data);
cout<<"product data done: "<<data<<endl;
sleep(1);
}
return nullptr;
}
int main()
{
BlockQueue bq;
pthread_t c, p;
pthread_create(&c, nullptr, Consumer, static_cast<void *>(&bq));
pthread_create(&p, nullptr, Producter, static_cast<void *>(&bq));
pthread_join(c, nullptr);
pthread_join(p, nullptr);
return 0;
}
POSIX信号量
POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步。
- 初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
//pshared:0表示线程间共享,非0表示进程间共享
//val:信号量初值
- 销毁
#include <semaphore.h>
int sem_destroy(sem_t *sem);
- 等待
#include <semaphore.h>
int sem_wait(sem_t *sem);
//等待信号量会将值减1
- 发布
#include <semaphore.h>
int sem_post(sem_t *sem);
//发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
基于环形队列的生产者消费者模型
#include<iostream>
#include<cstdlib>
#include<vector>
#include<time.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#define MAX 10
using namespace std;
class RingQueue
{
private:
vector<int> v;
size_t cap;
sem_t data_sem;
sem_t space_sem;
int consume_step;
int product_step;
public:
RingQueue(int _cap = MAX)
:v(_cap), cap(_cap), consume_step(0), product_step(0)
{
sem_init(&data_sem, 0, 0);
sem_init(&space_sem, 0, cap);
}
~RingQueue()
{
sem_destroy(&data_sem);
sem_destroy(&space_sem);
}
void pushData(const int& data)
{
sem_wait(&space_sem);
v[consume_step] = data;
consume_step++;
consume_step %= cap;
sem_post(&data_sem);
}
void popData(int &data)
{
sem_wait(&data_sem);
data = v[product_step];
product_step++;
product_step %= cap;
sem_post(&space_sem);
}
};
void * consumer(void * arg)
{
RingQueue * prq = static_cast<RingQueue *>(arg);
int data;
while(true)
{
prq->popData(data);
cout<<"consume data done: "<<data<<endl;
}
return nullptr;
}
void * producter(void * arg)
{
RingQueue * prq = static_cast<RingQueue *>(arg);
srand((unsigned int)time(nullptr));
while(true)
{
int data = rand() % 1024;
prq->pushData(data);
cout<<"product data done: "<<data<<endl;
sleep(1);
}
return nullptr;
}
int main()
{
RingQueue rq;
pthread_t c, p;
pthread_create(&c, nullptr, consumer, static_cast<void *>(&rq));
pthread_create(&p, nullptr, producter, static_cast<void *>(&rq));
pthread_join(c, nullptr);
pthread_join(p, nullptr);
return 0;
}