目录
1,前言介绍
2,生产消费模型
3,任务类
4,使用练习
5,结果
6,码云链接
什么意思呢,就是当生产者往阻塞队列生产数据,候生当队列满了的时产者等待,消费者消费数据同时唤醒生产者生产数据,当消费者消费数据阻塞队列为空时,消费者等待,生产者生产数据之后唤醒消费者。这里的等待指的是在各自的条件变量上面等待,这样我们就实现了一种同步关系,及消费和生产按照一定的关系,不是想读独立的,这里的具体如何同步也由我们自己控制、设计。在加上我们上节的锁来保护这个阻塞队列临界资源。
兄弟们,都在注释里面,非常详细了,就不浪费时间在一一描述每一个细节了,看注释哦。
#include
#include
#include
#include
using namespace std;
// 目标是设计一个基于阻塞队列的生产消费模型。
const uint32_t Defaultcap = 5;
template
class BlockQueue
{
public:
BlockQueue(uint32_t cap = Defaultcap)
: cap_(Defaultcap)
{
// 初始化互斥量和条件变量。
pthread_mutex_init(&lock_, nullptr);
pthread_cond_init(&client_cond_, nullptr);
pthread_cond_init(&ser_cond_, nullptr);
}
~BlockQueue()
{
// 销毁资源。
pthread_mutex_destroy(&lock_);
pthread_cond_destroy(&client_cond_);
pthread_cond_destroy(&ser_cond_);
}
public:
T push(const T &in)
{
// 1,访问临界资源=阻塞队列=加锁。
lockqueue();
// 2.为满,条件不满足,生产者不能生产,等待。
while (isfull())
{
ser_lock_wait();
}
//3. 条件满足,可以生产。
pushdata(in);
/// 4,生产完成,解锁。
unlockqueue();
// 这里西安解锁再唤醒消费者消费,尽量不要再锁中做锁外也可以做的事情,太浪费资源了。
// 5,唤醒消费者消费。
awaken_cond_client();
}
T pop()
{
//1,同理 加锁
lockqueue();
//2,条件不满足,无法消费,等待。
while(isempty())
{
cliend_lock_wait();
}
//3,条件满足,消费
T tmp=popdata();
//4,消费完成,立马解锁。
unlockqueue();
//5,唤醒生产者生产.
awaken_cond_ser();
//这里是必须要有的,忘记写了那就扯犊子了。
return tmp;
}
private:
// 加锁
void lockqueue()
{
pthread_mutex_lock(&lock_);
}
// 解锁
void unlockqueue()
{
pthread_mutex_unlock(&lock_);
}
// 判断阻塞队列是否位空。
bool isempty()
{
return queue_.empty();
}
// 判断阻塞队列是否为满
bool isfull()
{
return queue_.size() == cap_;
}
// 让消费者再消费者的条件变量等待
void cliend_lock_wait()
{
pthread_cond_wait(&client_cond_, &lock_);
}
// 让生产者再生产者的条件变量等待。
void ser_lock_wait()
{
pthread_cond_wait(&ser_cond_, &lock_);
}
// 唤醒消费者消费。
void awaken_cond_client()
{
pthread_cond_signal(&client_cond_);
}
// 唤醒生产者生产.
void awaken_cond_ser()
{
pthread_cond_signal(&ser_cond_);
}
void pushdata(const T& in)
{
queue_.push(in);
}
T popdata()
{
T tmp=queue_.front();
queue_.pop();
return tmp;
}
private:
int cap_; // 容量
queue queue_; // 保存任务的阻塞队列。
pthread_mutex_t lock_; // 保护阻塞队列的互斥量,锁。
pthread_cond_t client_cond_; // 消费者等待的条件变量。
pthread_cond_t ser_cond_; // 生产者等待的条件变量。
};
一个简单的+-*/%运算的任务类,并且他自己有处理任务的方法,同时,兄弟们,看注释哦。
#pragma once
#include
#include
// 一个任务类,并且他自己实现了处理任务的方法。
class Task
{
public:
Task(int one = 1, int two = 1, char op = '*')
: one_(one), two_(two), op_(op)
{
}
// 析构不需要写。
// 处理任务
int run()
{
int resault = -1;
switch (op_)
{
case '+':
resault = one_ + two_;
break;
case '-':
resault = one_ - two_;
break;
case '*':
resault = one_ * two_;
break;
case '/':
if (two_ == 0)
{
resault = -1;
flag = false;
}
else
{
resault = one_ / two_;
}
break;
case '%':
if (two_ == 0)
{
resault = -1;
flag = false;
}
else
{
resault = one_ % two_;
}
break;
default:
flag = false;
resault = -1;
break;
}
return resault;
}
// 用算符重载。
int operator() ()
{
return run();
}
// 把需要处理的任务用输出型参数带回,方便打印。
void getdata(int *one, int *two, char *op)
{
*one = one_;
*two = two_;
*op = op_;
}
bool istrue()
{
return flag;
}
private:
int one_;
int two_;
char op_;
bool flag = true;
};
搞了俩个线程,一个生产数据,一个消费数据,主要是看他的同步互斥关系。互斥关系由一把锁维护的哦,兄弟们,看注释,不多讲了。
#include "blockqueue.hpp"
#include "Task.hpp"
#include
string ops = "+-*/%";
void *consumer(void *args)
{
BlockQueue *q = (BlockQueue *)args;
while (true)
{
sleep(1);
// 1,只管消费任务就完了。
Task tmp = q->pop();
// 2,处理任务。
int resault = tmp();
int one;
int two;
char op;
// 带回是什么任务。
tmp.getdata(&one, &two, &op);
if (tmp.istrue())
cout << "处理正常任务:" << one << op << two << "=" << resault << endl;
else
cout << "处理异常任务:" << one << op << two << "=" << resault << endl;
}
}
void *productor(void *args)
{
BlockQueue *q = (BlockQueue *)args;
while (true)
{
sleep(1);
// 1,只管生产任务就好了。
int one_;
int two_;
char op_;
one_=rand()%10;
two_=rand()%10;
op_=ops[rand()%ops.size()];
Task tmp(one_, two_, op_);
q->push(tmp);
cout<<"productor生产了一个任务: "< dp;
pthread_create(&t1,nullptr,consumer,&dp);
pthread_create(&t1,nullptr,productor,&dp);
pthread_join(t1,nullptr);
pthread_join(t2,nullptr);
cout<<"线程退出"<
我们可以sleep来调整生产数据和使用数据的时间,都是可以的,不会乱的。
lyh_linux-test: linux代码练习。 - Gitee.com