首先了解两个概念:1. promise和future 2. 条件变量
Promise和Future:
promise对象就是一个任务,有人(线程)去接这项任务并且承诺未来某一时刻会完成这项任务并做一个回馈,而future就是任务发起者用来接收promise完成结果的东西。
条件变量:
1. 条件变量的使用场景:
头文件#include<condition_variable>
2. 条件变量使用流程图例:
以下图为例:主线程是小红做作业,t0线程是小红妈妈给他买麦当劳(买麦当劳这一动作定义为func函数)。但只有当妈妈达到了一定的满意状态才会给买,cv就是这个过程中定义的条件变量,流程为:
1. 小红做作业做到十点,询问了一次妈妈(cv.notify_one()),妈妈表示不满意,才做了这么一会就想吃麦当劳,给了一巴掌。
2. 小红继续做作业到下午一点,妈妈还是不满意,如上。
3. 终于小红做到半夜十一点,妈妈终于满意,执行了t0线程的func,买了麦当劳。
这期间cv.wait(lock,[&](){满意状态})就是妈妈的情绪是否到达阈值状态的一个询问,满意了才会执行t0线程的相关的相关动作。
然后进行生产者——消费者模型的介绍
生产者——消费者模型:
生产者消费者问题是一个经典的多线程同步问题。该问题描述了两个进程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是不断的生成数据,而与此同时,消费者则不断消耗这些数据。该问题的关键就是要保证当生产者生产了产品后,若消费者还没有消费此产品,则生产者停止生产并等待,直到消费者消费了此产品;当消费者消费了产品后,若生产者还没有及时生产新的产品,则消费者停止消费并等待,直到生产者生产了新产品。
#include<thread>
#include<queue>
#include<mutex>//锁
#include<string>
#include<stdio.h>
#include<chrono>
#include<condition_variable>
#include<memory>
#include<future>
using namespace std;
//贡献资源访问问题
//queue, stl对象不是线程安全,需要用锁
//如果生产速度大于消费速度,会产生堆积,需要设置溢出限制
//即队列满了,不生产,等待队列有空间在生产
//push之前对队列进行判断,通过条件变量实现
struct Job{
shared_ptr<promise<string>> pro;
string input;
};
queue<Job> qjobs_;
mutex lock_;
condition_variable cv_;
const int limit = 5;
//生产者
void video_capture(){
int pic_id=0;
while(true){
Job job;
{
//生产加锁,作用范围不包括等待
unique_lock<mutex> l(lock_);
char name[100];
sprintf(name,"PIC-%d",pic_id++);
printf("生产了新图片:%s, qjobs_.size= %d\n",name, qjobs_.size());
//一旦进入wait则解锁,一旦退出wait则加锁
cv_.wait(l,[&](){
//return false, 则继续等待
//return true,则不等待,跳出wait
return qjobs_.size() < limit;
});
//消费者有多个,对应到应用场景中可能是多个模型(检测、分割等等),分发数据到不同模型中异步进行在同一回收增大效率,需要从消费者中拿回数据
job.pro.reset(new promise<string>());
job.input = name;
qjobs_.push(job);
//拿消费者返回的结果
//.get过后实现等待,等待消费者执行set_value,拿到结果
}
auto result = job.pro->get_future().get();
printf("pic %s -> %s",job.input.c_str(),result.c_str());
this_thread::sleep_for(chrono::milliseconds(500));
}
}
//消费者
void infer_worker(){
while(true){
if(!qjobs_.empty()){
{
unique_lock<mutex> l(lock_);//消费加锁,作用不包含等待,等待的过程就是另一线程填充和消费的时候,此时线程安全
auto pic = qjobs_.front();
qjobs_.pop();
//每消费一个就通知cv_判断
cv_.notify_one();
printf("消费了图片:%s\n",pic.input.c_str());
//对应处理过程
auto new_pic = pic.input+"---infer";
//返回生产者
pic.pro->set_value(new_pic);
}
this_thread::sleep_for(chrono::milliseconds(1000));
}
//不执行代码时交出控制权
this_thread::yield();
}
}
int main(){
thread t0(video_capture);
thread t1(infer_worker);
t0.join();
t1.join();
return 0;
}