任务:
实现多个生产者往阻塞队列里面写入数据、一个消费者从阻塞队列中读取数据(阻塞队列也可以叫做用户级别的消息队列)
模型:
1.角色:生产者、消费者
2.关系:
生产者vs生产者:互斥关系,一次只有一个线程能够往阻塞队列里面写数据
生产者vs消费者:之间的关系是同步的、生产者将数据填满阻塞队列才会叫消费者去读取,消费者读完数据,阻塞队列为空才会叫生产者去生产
3.场所:
交易场所是阻塞队列
#pragma once
#include
#include
#include
using namespace std;
#include
class Test
{
public:
int x;
int y;
};
template<class T>
class BlockQueue
{
private:
queue <T> _q;//阻塞队列
size_t _cap;//容量
pthread_mutex_t lock;//锁
pthread_cond_t p_cond;//生产者条件变量
pthread_cond_t c_cond;//消费者条件变量
public:
BlockQueue(size_t cap)
:_cap(cap)
{
pthread_mutex_init(&lock,nullptr);
pthread_cond_init(&p_cond,nullptr);
pthread_cond_init(&c_cond,nullptr);
}
~BlockQueue()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&p_cond);
pthread_cond_destroy(&c_cond);
}
void LockQueue()
{
pthread_mutex_lock(&lock);
}
void unLockQueue()
{
pthread_mutex_unlock(&lock);
}
bool Full()
{
return _q.size()>=_cap;//满了
}
bool Empty()
{
return _q.empty();
}
void WakeUpConsumer()
{
cout<<"wake up consemer.....\n";
pthread_cond_signal(&c_cond);
}
void WakeUpProducter()
{
cout<<"wake up producter......\n";
pthread_cond_signal(&p_cond);
}
void ConsumerWait()
{
cout<<"consumer wait.....\n";
pthread_cond_wait(&c_cond,&lock);
}
void ProducterWait()
{
cout<<"producter wait.......\n";
pthread_cond_wait(&p_cond,&lock);
}
void Put(const T &data)//放数据
{
LockQueue();//加锁
while(Full())//满起自己,通知消费者
{
WakeUpConsumer();//队列满了,唤醒消费者
ProducterWait();//自己挂起
}
_q.push(data);
unLockQueue();//解锁
}
void Take(T &data)//拿数据,输出型参数
{
LockQueue();
while(Empty())//使用while防止异常、没有挂起
{
WakeUpProducter();//唤醒生产者
ConsumerWait();//自己挂起
}
data=_q.front();
_q.pop();
unLockQueue();
}
};
```cpp
#include"bq.hpp"
pthread_mutex_t lock;
void *PutData(void *arg)
{
BlockQueue<Test> *q=(BlockQueue<Test>*)arg;
while(1)
{
pthread_mutex_lock(&lock);//加锁
cout<<"thread"<<pthread_self()<<"put data"<<endl;//当前是那个生产者
Test data;
data.x=rand()%5;
data.y=rand()%10;
q->Put(data);//将随机生成的数据插入阻塞队列之中
cout<<"put data:"<<data.x<<"+"<<data.y<<"=?"<<endl;
pthread_mutex_unlock(&lock);//解锁
usleep(1);//线程切换是在内核态回用户态时,增加系统调用,加多判断
}
}
void *GetData(void *arg)
{
BlockQueue<Test> *q=(BlockQueue<Test>*)arg;
while(1)
{
sleep(1);
Test data;
q->Take(data);
cout<<"get data:"<<data.x<<"+"<<data.y<<"="<<data.x+data.y<<endl;//打印拿到的数据
}
}
int main()
{
pthread_t tid1,tid2,tid3,tid4;
BlockQueue<Test> *q =new BlockQueue<Test>(5);//也可以定义成全局变量
pthread_mutex_init(&lock,nullptr);//多个生产者,实现互斥
pthread_create(&tid1,nullptr,GetData,(void*)q);//消费者拿数据
pthread_create(&tid2,nullptr,PutData,(void*)q);//生产者生产数据
pthread_create(&tid3,nullptr,PutData,(void*)q);//生产者生产数据
pthread_create(&tid4,nullptr,PutData,(void*)q);//生产者生产数据
//等待不关心返回值
pthread_join(tid1,nullptr);
pthread_join(tid2,nullptr);
pthread_join(tid3,nullptr);
pthread_join(tid4,nullptr);
pthread_mutex_destroy(&lock);
delete q;
return 0;
}
POSIX信号量和System V信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步
信号量(信号灯)本质是一种计数器,描述临界资源有效个数的计数器
举个例子:
场景:电影院(临界区)
进入对象:人(线程)
竞争资源:座位(临界资源)
座位计数器:票数(计数器)
假设电影是无限开放的,并且异常火爆,只有买到票的人才可以进入观看,这时为了维护秩序,就需要用票数来控制进入影院的人数,一张票对应一个座椅,有人进入则票数减一, 有人出来则票数加一
将临界资源看成多份之后,可以使得多个线程进入临界区访问临界资源,并且不发生冲突,因此可以提高效率
而信号灯的作用就是维护线程之间的互斥关系,确保进入的线程不会超过临界资源的个数
销毁信号量:
等待信号量:
等待信号量,信号量的值-1
发布信号量:
表示资源使用完毕,归还资源,信号量的值+1
#pragma once
#include
#include
#include
#include
#include
#define NUM 5
class RingQueue
{
private:
std::vector<int> _q;
int _cap;
//信号量
sem_t sem_blank;
sem_t sem_data;
//资源下标
int pro_sub;
int con_sub;
private:
void P(sem_t &sem)
{
sem_wait(&sem);
}
void V(sem_t &sem)
{
sem_post(&sem);
}
public:
RingQueue(int cap=NUM)
:_cap(cap)
,_q(cap)
{
sem_init(&sem_blank,0,_cap);//格子个数一开始为容量大小
sem_init(&sem_data,0,0);//数据一开始为0
pro_sub=0;
con_sub=0;
}
void Put(const int &data)
{
P(sem_blank);//申请格子资源,判断是否还有容量,格子--
_q[pro_sub]=data;
pro_sub++;
pro_sub%=_cap;//越界回环
V(sem_data);//数据++
}
void Get(int &data)
{
P(sem_data);//申请数据资源,判断是否还有数据,数据--
data=_q[con_sub];
con_sub++;
con_sub%=_cap;
V(sem_blank);//消耗数据,格子++
}
~RingQueue()
{
sem_destroy(&sem_blank);
sem_destroy(&sem_data);
pro_sub=con_sub=0;
}
};
#include "RingQueue.hpp"
pthread_mutex_t pro_lock;//生产者组内竞争锁
pthread_mutex_t con_lock;//消费者组内竞争锁
int count=0;//生产者数据
void Lock(pthread_mutex_t &lock)
{
pthread_mutex_lock(&lock);
}
void ULock(pthread_mutex_t &lock)
{
pthread_mutex_unlock(&lock);
}
void *Get(void *arg)
{
RingQueue *q=(RingQueue*)arg;
while(1)
{
usleep(1);
int data=0;
Lock(con_lock);//组内竞争
q->Get(data);//获取数据
ULock(con_lock);
std::cout<<pthread_self()<<":consumer get data...:"<<data<<std::endl;
}
}
void *Put(void *arg)
{
RingQueue *q=(RingQueue*)arg;
while(1)
{
sleep(1);//增加系统调用
Lock(pro_lock);//组内竞争
q->Put((++count)%10);
ULock(pro_lock);
std::cout<<pthread_self()<<":productor put data...."<<std::endl;
}
}
int main()
{
//创建交易场所
RingQueue *q=new RingQueue(5);
//初始化锁
pthread_mutex_init(&con_lock,nullptr);
pthread_mutex_init(&pro_lock,nullptr);
//创建线程
pthread_t tid1,tid2,tid3,tid4,tid5,tid6;
pthread_create(&tid1,nullptr,Put,q);
pthread_create(&tid2,nullptr,Put,q);
pthread_create(&tid3,nullptr,Put,q);
pthread_create(&tid4,nullptr,Get,q);
pthread_create(&tid5,nullptr,Get,q);
pthread_create(&tid6,nullptr,Get,q);
//等待线程、避免内存泄漏,不关心返回值
pthread_join(tid1,nullptr);
pthread_join(tid2,nullptr);
pthread_join(tid3,nullptr);
pthread_join(tid4,nullptr);
pthread_join(tid5,nullptr);
pthread_join(tid6,nullptr);
//销毁工作
pthread_mutex_destroy(&con_lock);
pthread_mutex_destroy(&pro_lock);
delete q;
return 0;
}