一.上集回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:
二.条件变量的简单demo
1.上集回顾
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
int ticket = 0;
pthread_mutex_t mutex;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *route(void *arg)
{
char *id = (char*)arg;
while (1) {
pthread_mutex_lock(&mutex);
if (ticket > 0) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&mutex);
} else {
printf("%s wait on cond!\n",id);
pthread_cond_wait(&cond,&mutex);//醒来的时候,会重新申请锁!!
printf("%s 被叫醒了\n",id);
}
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, route, (void*)"thread_1");
pthread_create(&t2, NULL, route, (void*)"thread_2");
pthread_create(&t3, NULL, route, (void*)"thread_3");
pthread_create(&t4, NULL, route, (void*)"thread_4");
int cnt = 10;
while(true)
{
sleep(5);
ticket += cnt;
printf("主线程放票咯! ticket: %d\n",ticket);
pthread_cond_signal(&cond);
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
不是只有一个线程能拿到对应的锁吗?为啥都会在条件变量下等呢?


2.简单demo
cpp
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <string>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* active(void* arg)
{
std::string name = static_cast<const char*>(arg);
while(true)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf("%s isactive!\n",name.c_str());
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,nullptr,active,(void*)"thread-1");
pthread_create(&tid2,nullptr,active,(void*)"thread-2");
pthread_create(&tid3,nullptr,active,(void*)"thread-3");
sleep(1);
printf("Main thread ctrl begin...\n");
while(true)
{
printf("main wakup thread...\n");
pthread_cond_signal(&cond);
sleep(1);
}
pthread_join(tid1,nullptr);
pthread_join(tid2,nullptr);
pthread_join(tid3,nullptr);
return 0;
}


cpp
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <string>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* active(void* arg)
{
std::string name = static_cast<const char*>(arg);
while(true)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf("%s isactive!\n",name.c_str());
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,nullptr,active,(void*)"thread-1");
pthread_create(&tid2,nullptr,active,(void*)"thread-2");
pthread_create(&tid3,nullptr,active,(void*)"thread-3");
sleep(1);
printf("Main thread ctrl begin...\n");
while(true)
{
printf("main wakup thread...\n");
pthread_cond_broadcast(&cond);
sleep(1);
}
pthread_join(tid1,nullptr);
pthread_join(tid2,nullptr);
pthread_join(tid3,nullptr);
return 0;
}

三.生产者消费者模型





四.基于BlockingQueue的生产者消费者模型


1.单生成,单消费代码实现
a.main函数的实现
cpp
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace BlockQueueModule;
void* Consumer(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while(true)
{
int data;
//1.从bq拿到数据
bq->Pop(&data);
//2.做处理
sleep(1);
printf("Consumer, get a data: %d\n",data);
}
}
void* Productor(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while(true)
{
//1.从外部获取数据
int data = 10;
//2.生产到bq中
bq->Equeue(data);
}
}
int main()
{
BlockQueue<int>* bq = new BlockQueue<int>();//共享资源 -> 临界资源
//单生产,单消费
pthread_t c,p;
pthread_create(&c,nullptr,Consumer,bq);
pthread_create(&p,nullptr,Productor,bq);
pthread_join(c,nullptr);
pthread_join(p,nullptr);
delete bq;
return 0;
}

b.blockqueue的大致框架
cpp
#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
namespace BlockQueueModule
{
template<class T>
class BlockQueue
{
public:
BlockQueue()
{
}
void Equeue(const T& in)
{
}
void Pop(T* out)
{
}
~BlockQueue()
{
}
private:
std::queue<T> _q; //保存数据的容器
int _cap; //bq最大容量
pthread_mutex_t _mutex; //互斥
pthread_cond_t _productor_cond;//生产者的条件变量
pthread_cond_t _consumer_cond; //消费者的条件变量
};
}
所以的线程互斥,线程同步的机制,都在我们对应的Equeue和Pop里面进行完成
c.BlockQueue函数的实现
cpp
BlockQueue(int cap = gcap):_cap(cap)
{
pthread_mutex_init(&_mutex,nullptr);
pthread_cond_init(&_productor_cond,nullptr);
pthread_cond_init(&_consumer_cond,nullptr);
}
d.~BlockQueue函数的实现
cpp
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_productor_cond);
pthread_cond_destroy(&_consumer_cond);
}
e.其他函数的实现
cpp
bool IsFull()
{
return _q.size() == _cap;
}
bool IsEmpty()
{
return _q.empty();
}
f.Equeue函数的实现
cpp
void Equeue(const T& in)
{
pthread_mutex_lock(&_mutex);
//想放数据也是要有条件的
//1.在临界区中等待是必然的!
if(IsFull())
{
//2.等待的时候,会释放_mutex
pthread_cond_wait(&_productor_cond,&_mutex);//wait的时候,必定是持有锁的
//3.返回,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来)
}
//4.到这里,我们一定可以进行生产了
_q.push(in);
pthread_mutex_unlock(&_mutex);
}

g.Pop函数的实现
cpp
void Pop(T* out)
{
pthread_mutex_lock(&_mutex);
if(IsEmpty())
{
pthread_cond_wait(&_consumer_cond,&_mutex);
}
//到这里我们必定有数据
*out = _q.fornt();
_q.pop();
pthread_mutex_unlock(&_mutex);
}
这个Pop函数和上述的Equeue函数差不多
2.代码debug
上述的Equeue和我们的Pop有没有那里不对呢?
上述代码全部都在进行休眠,但是谁来将他们唤醒呢?
当没有资源时,消费者在进行等待,那么谁只有会有资源呢?(答案是生产者,所以当我们进行生产的时候,就将等待的消费者进行唤醒(前提是要有消费者),生产者等待时也同理)
所有,我们可以加上两个成员函数,来统计我们的生产者和消费者等待的个数
cpp
int _cwait_num;
int _pwait_num;
cpp
BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
{
pthread_mutex_init(&_mutex,nullptr);
pthread_cond_init(&_productor_cond,nullptr);
pthread_cond_init(&_consumer_cond,nullptr);
}
a.Equeue代码更新
cpp
void Equeue(const T& in)
{
pthread_mutex_lock(&_mutex);
//想放数据也是要有条件的
//1.在临界区中等待是必然的!
if(IsFull())
{
std::cout << "生产者进入等待..." << std::endl;
//2.等待的时候,会释放_mutex
_pwait_num++;
pthread_cond_wait(&_productor_cond,&_mutex);//wait的时候,必定是持有锁的
_pwait_num--;
//3.返回,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来)
std::cout << "生产者被唤醒..." << std::endl;
}
//4.到这里,我们一定可以进行生产了
_q.push(in);
//一定有数据
if(_cwait_num)
{
pthread_cond_signal(&_consumer_cond);
}
pthread_mutex_unlock(&_mutex);
}
b.Pop代码更新
cpp
void Pop(T* out)
{
pthread_mutex_lock(&_mutex);
if(IsEmpty())
{
std::cout << "消费者进入等待..." << std::endl;
_cwait_num++;
pthread_cond_wait(&_consumer_cond,&_mutex);
_cwait_num--;
std::cout << "消费者被唤醒..." << std::endl;
}
//到这里我们必定有数据
*out = _q.fornt();
_q.pop();
//一定有空间
if(_pwait_num)
{
pthread_cond_signal(&_productor_cond);
}
pthread_mutex_unlock(&_mutex);
}
c.生产者无限生产,消费者慢一点进行消费
cpp
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace BlockQueueModule;
void* Consumer(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while(true)
{
sleep(2);
int data;
//1.从bq拿到数据
bq->Pop(&data);
//2.做处理
printf("Consumer, 消费了一个数据: %d\n",data);
}
}
void* Productor(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
int data = 10;
while(true)
{
//1.从外部获取数据
//2.生产到bq中
bq->Equeue(data);
printf("productor 生产了一个数据: %d\n",data);
data++;
}
}
int main()
{
BlockQueue<int>* bq = new BlockQueue<int>(5);//共享资源 -> 临界资源
//单生产,单消费
pthread_t c,p;
pthread_create(&c,nullptr,Consumer,bq);
pthread_create(&p,nullptr,Productor,bq);
pthread_join(c,nullptr);
pthread_join(p,nullptr);
delete bq;
return 0;
}

消费的是历史数据,生产的是新的数据
d.消费者先进行消费,然后生产者再进行生产
cpp
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace BlockQueueModule;
void* Consumer(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while(true)
{
int data;
//1.从bq拿到数据
bq->Pop(&data);
//2.做处理
printf("Consumer, 消费了一个数据: %d\n",data);
}
}
void* Productor(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
int data = 10;
while(true)
{
sleep(2);
//1.从外部获取数据
//2.生产到bq中
bq->Equeue(data);
printf("productor 生产了一个数据: %d\n",data);
data++;
}
}
int main()
{
BlockQueue<int>* bq = new BlockQueue<int>(5);//共享资源 -> 临界资源
//单生产,单消费
pthread_t c,p;
pthread_create(&c,nullptr,Consumer,bq);
pthread_create(&p,nullptr,Productor,bq);
pthread_join(c,nullptr);
pthread_join(p,nullptr);
delete bq;
return 0;
}


在解锁之前和解锁之后等,都是可以的
单生成单消费这里,我们是不会出现啥问题的
五.多生产,多消费
1.代码整体修改
cpp
"BlockQueue.hpp"
#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
namespace BlockQueueModule
{
static const int gcap = 10;
template<class T>
class BlockQueue
{
private:
bool IsFull()
{
return _q.size() == _cap;
}
bool IsEmpty()
{
return _q.empty();
}
public:
BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
{
pthread_mutex_init(&_mutex,nullptr);
pthread_cond_init(&_productor_cond,nullptr);
pthread_cond_init(&_consumer_cond,nullptr);
}
void Equeue(const T& in)
{
pthread_mutex_lock(&_mutex);
//想放数据也是要有条件的
//1.在临界区中等待是必然的!
if(IsFull())
{
std::cout << "生产者进入等待..." << std::endl;
//2.等待的时候,会释放_mutex
_pwait_num++;
pthread_cond_wait(&_productor_cond,&_mutex);//wait的时候,必定是持有锁的
_pwait_num--;
//3.返回,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来)
std::cout << "生产者被唤醒..." << std::endl;
}
//4.到这里,我们一定可以进行生产了
_q.push(in);
//一定有数据
if(_cwait_num)
{
pthread_cond_signal(&_consumer_cond);
}
pthread_mutex_unlock(&_mutex);
}
void Pop(T* out)
{
pthread_mutex_lock(&_mutex);
if(IsEmpty())
{
std::cout << "消费者进入等待..." << std::endl;
_cwait_num++;
pthread_cond_wait(&_consumer_cond,&_mutex);
_cwait_num--;
std::cout << "消费者被唤醒..." << std::endl;
}
//到这里我们必定有数据
*out = _q.front();
_q.pop();
//一定有空间
if(_pwait_num)
{
pthread_cond_signal(&_productor_cond);
}
pthread_mutex_unlock(&_mutex);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_productor_cond);
pthread_cond_destroy(&_consumer_cond);
}
private:
std::queue<T> _q; //保存数据的容器
int _cap; //bq最大容量
pthread_mutex_t _mutex; //互斥
pthread_cond_t _productor_cond;//生产者的条件变量
pthread_cond_t _consumer_cond; //消费者的条件变量
int _cwait_num;
int _pwait_num;
};
}
这里如果是单生成单消费,那么就没有问题,但是如果是我们对应的多生产和多消费,那么这样就行不通了,因为我们唤醒,是要将所有的代码进行唤醒,而不是只进行唤醒一个线程

cpp
#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
namespace BlockQueueModule
{
static const int gcap = 10;
template<class T>
class BlockQueue
{
private:
bool IsFull()
{
return _q.size() == _cap;
}
bool IsEmpty()
{
return _q.empty();
}
public:
BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
{
pthread_mutex_init(&_mutex,nullptr);
pthread_cond_init(&_productor_cond,nullptr);
pthread_cond_init(&_consumer_cond,nullptr);
}
void Equeue(const T& in)
{
pthread_mutex_lock(&_mutex);
//想放数据也是要有条件的
//1.在临界区中等待是必然的!
while(IsFull())
{
std::cout << "生产者进入等待..." << std::endl;
//2.等待的时候,会释放_mutex
_pwait_num++;
pthread_cond_wait(&_productor_cond,&_mutex);//wait的时候,必定是持有锁的
_pwait_num--;
//3.返回,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来)
std::cout << "生产者被唤醒..." << std::endl;
}
//4.到这里,我们一定可以进行生产了
_q.push(in);
//一定有数据
if(_cwait_num)
{
pthread_cond_signal(&_consumer_cond);
}
pthread_mutex_unlock(&_mutex);
}
void Pop(T* out)
{
pthread_mutex_lock(&_mutex);
while(IsEmpty())
{
std::cout << "消费者进入等待..." << std::endl;
_cwait_num++;
pthread_cond_wait(&_consumer_cond,&_mutex);//伪唤醒
_cwait_num--;
std::cout << "消费者被唤醒..." << std::endl;
}
//到这里我们必定有数据
*out = _q.front();
_q.pop();
//一定有空间
if(_pwait_num)
{
pthread_cond_signal(&_productor_cond);
}
pthread_mutex_unlock(&_mutex);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_productor_cond);
pthread_cond_destroy(&_consumer_cond);
}
private:
std::queue<T> _q; //保存数据的容器
int _cap; //bq最大容量
pthread_mutex_t _mutex; //互斥
pthread_cond_t _productor_cond;//生产者的条件变量
pthread_cond_t _consumer_cond; //消费者的条件变量
int _cwait_num;
int _pwait_num;
};
}

cpp
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace BlockQueueModule;
void* Consumer(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while(true)
{
int data;
//1.从bq拿到数据
bq->Pop(&data);
//2.做处理
printf("Consumer, 消费了一个数据: %d\n",data);
}
}
void* Productor(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
int data = 10;
while(true)
{
sleep(2);
//1.从外部获取数据
//2.生产到bq中
bq->Equeue(data);
printf("productor 生产了一个数据: %d\n",data);
data++;
}
}
int main()
{
BlockQueue<int>* bq = new BlockQueue<int>(5);//共享资源 -> 临界资源
//单生产,单消费
pthread_t c1,c2,p1,p2,p3;
pthread_create(&c1,nullptr,Consumer,bq);
pthread_create(&c2,nullptr,Consumer,bq);
pthread_create(&p1,nullptr,Productor,bq);
pthread_create(&p2,nullptr,Productor,bq);
pthread_create(&p3,nullptr,Productor,bq);
pthread_join(c1,nullptr);
pthread_join(c2,nullptr);
pthread_join(p1,nullptr);
pthread_join(p2,nullptr);
pthread_join(p3,nullptr);
delete bq;
return 0;
}
这里我们创建了2个生产者,3个消费者(我们刚刚改了while,所以支持多生产多消费了)

2.封装(mutex)条件变量
a.条件变量框架搭建
cpp
#pragma once
#include <iostream>
#include <pthread.h>
namespace CondModule
{
class Cond
{
public:
Cond()
{
int n = ::pthread_cond_init(&_cond,nullptr);
(void)n;
}
~Cond()
{
int n = ::pthread_cond_destroy(&_cond);
}
private:
pthread_cond_t _cond;
};
}
b.其他函数的封装
cpp
#pragma once
#include <iostream>
#include <pthread.h>
#include "Mutex.hpp"
namespace CondModule
{
using namespace LockModule;
class Cond
{
public:
Cond()
{
int n = ::pthread_cond_init(&_cond,nullptr);
(void)n;
}
void Wait(Mutex& mutex)//让线程曾经的锁释放曾经的锁
{
int n = ::pthread_cond_wait(&_cond,mutex.LockPtr());
(void)n;
}
void Notify()
{
int n = ::pthread_cond_signal(&_cond);
(void)n;
}
void NotifyAll()
{
int n = ::pthread_cond_broadcast(&_cond);
(void)n;
}
~Cond()
{
int n = ::pthread_cond_destroy(&_cond);
(void)n;
}
private:
pthread_cond_t _cond;
};
}
3.封装BlockQueue
cpp
#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"
namespace BlockQueueModule
{
using namespace LockModule;
using namespace CondModule;
static const int gcap = 10;
template<class T>
class BlockQueue
{
private:
bool IsFull()
{
return _q.size() == _cap;
}
bool IsEmpty()
{
return _q.empty();
}
public:
BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
{
}
void Equeue(const T& in)
{
LockGuard lockguard(_mutex);
while(IsFull())
{
std::cout << "生产者进入等待..." << std::endl;
_pwait_num++;
_productor_cond.Wait(_mutex);
_pwait_num--;
std::cout << "生产者被唤醒..." << std::endl;
}
_q.push(in);
if(_cwait_num)
{
_consumer_cond.Notify();
}
}
void Pop(T* out)
{
LockGuard lockguard(_mutex);
while(IsEmpty())
{
std::cout << "消费者进入等待..." << std::endl;
_cwait_num++;
_consumer_cond.Wait(_mutex);
_cwait_num--;
std::cout << "消费者被唤醒..." << std::endl;
}
//到这里我们必定有数据
*out = _q.front();
_q.pop();
//一定有空间
if(_pwait_num)
{
_productor_cond.Notify();
}
}
~BlockQueue()
{
}
private:
std::queue<T> _q; //保存数据的容器
int _cap; //bq最大容量
Mutex _mutex; //互斥
Cond _productor_cond;//生产者的条件变量
Cond _consumer_cond; //消费者的条件变量
int _cwait_num;
int _pwait_num;
};
}


六.传递任务
1.场景1
cpp
"Task.hpp"
#pragma once
#include <iostream>
#include <unistd.h>
namespace TaskModule
{
class Task
{
public:
Task()
{}
Task(int a,int b):x(a),y(b)
{
}
void Excute()
{
sleep(1);//模拟任务处理时长
result = x + y;
}
int X()
{
return x;
}
int Y()
{
return y;
}
int Result()
{
return result;
}
~Task()
{}
private:
int x;
int y;
int result;
};
}
cpp
#include "BlockQueue.hpp"
#include "Task.hpp"
#include <pthread.h>
#include <unistd.h>
#include <ctime>
using namespace BlockQueueModule;
using namespace TaskModule;
void* Consumer(void* args)
{
BlockQueue<Task>* bq = static_cast<BlockQueue<Task>*>(args);
while(true)
{
Task t;
//1.从bq拿到数据
bq->Pop(&t);
//2.做处理
t.Excute();
printf("Consumer, 处理了一个任务: %d+%d=%d\n",t.X(),t.Y(),t.Result());
}
}
void* Productor(void* args)
{
BlockQueue<Task>* bq = static_cast<BlockQueue<Task>*>(args);
int data = 10;
while(true)
{
//1.从外部获取数据
int x = rand() % 10 + 1;
int y = rand() % 20 + 1;
Task t(x,y);
//2.生产到bq中
bq->Equeue(t);
printf("productor 生产了一个任务: %d+%d=?\n",t.X(),t.Y());
}
}
int main()
{
srand(time(nullptr) ^ getpid());
BlockQueue<Task>* bq = new BlockQueue<Task>(5);//共享资源 -> 临界资源
//单生产,单消费
pthread_t c1,c2,p1,p2,p3;
pthread_create(&c1,nullptr,Consumer,bq);
pthread_create(&c2,nullptr,Consumer,bq);
pthread_create(&p1,nullptr,Productor,bq);
pthread_create(&p2,nullptr,Productor,bq);
pthread_create(&p3,nullptr,Productor,bq);
pthread_join(c1,nullptr);
pthread_join(c2,nullptr);
pthread_join(p1,nullptr);
pthread_join(p2,nullptr);
pthread_join(p3,nullptr);
delete bq;
return 0;
}


2.场景2
cpp
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <functional>
void test()
{
std::cout << "haha test..." << std::endl;
}
void hello()
{
std::cout << "hehe hello..." << std::endl;
}
using task_t = std::function<void()>;
using namespace BlockQueueModule;
void* Consumer(void* args)
{
BlockQueue<task_t>* bq = static_cast<BlockQueue<task_t>*>(args);
while(true)
{
task_t t;
//1.从bq拿到数据
bq->Pop(&t);
//2.做处理
t();
}
}
void* Productor(void* args)
{
BlockQueue<task_t>* bq = static_cast<BlockQueue<task_t>*>(args);
int data = 10;
while(true)
{
sleep(1);
bq->Equeue(test);
}
}
int main()
{
srand(time(nullptr) ^ getpid());
BlockQueue<task_t>* bq = new BlockQueue<task_t>(5);//共享资源 -> 临界资源
//单生产,单消费
pthread_t c1,c2,p1,p2,p3;
pthread_create(&c1,nullptr,Consumer,bq);
pthread_create(&c2,nullptr,Consumer,bq);
pthread_create(&p1,nullptr,Productor,bq);
pthread_create(&p2,nullptr,Productor,bq);
pthread_create(&p3,nullptr,Productor,bq);
pthread_join(c1,nullptr);
pthread_join(c2,nullptr);
pthread_join(p1,nullptr);
pthread_join(p2,nullptr);
pthread_join(p3,nullptr);
delete bq;
return 0;
}

7.补充
生产者消费者,要串行执行那么高效在哪里呢?
生产者在获取任务的时候,消费者可以从区域上拿任务,还可以进行处理任务
消费者处理数据的时候,生产者可以从外部拿数据

