#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>
using namespace std;
condition_variable cv;
mutex mtx;
deque<int>q;
//生产者线程
void prod(){
int i=0;
while(true){
unique_lock<mutex> lock(mtx);
q.emplace_back(i);
//随机唤醒某一个线程
cv.notify_one();
// 去唤醒所有线程
// cv.notify_all();
//每次生产出一个数据后,就去唤醒 cv_wait
if(i<999999){
i++;
}
else{
i=0;
}
}
}
//消费者线程1
void con1(){
int data = 0;
while(true){
unique_lock<mutex>lock(mtx);
//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁
while(q.empty()){
//可能有多个线程调用了这个cv_wait,所以会导致报错
cv.wait(lock);
//含义如下,
//lock.unlock()
//cv.wait()
}
data = q.front();
q.pop_front();
cout<<"Get value from que:"<<data<<endl;
}
}
//消费者线程2
//如果此时增加了一个消费者线程,将会出现问题
//虚假唤醒问题 即队列为空,但是消费者线程却被唤醒了,去取队列里面的值,就报错了、
//发生错误的情形如下:
//在线程1中,此时队列q中有一个值,线程1正常pop,然后打印,接着进入到下一个循环,运行到 unique_lock<mutex>lock(mtx);时
//此时队列 元素+1,然后随机的唤醒了线程2,但此时线程1拥有队列的锁,于是线程1拿到了队列中的值,然后进行的pop,此时队列为空,
//此时线程2再去拿队列中的值,就会发生异常,只需要将 if 修改为while
void con2(){
int data = 0;
while(true){
unique_lock<mutex>lock(mtx);
//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁
while(q.empty()){
cv.wait(lock);
}
data = q.front();
q.pop_front();
cout<<"Get value from que:"<<data<<endl;
}
}
int main(){
thread t1(prod);
thread t2(con1);
thread t3(con2);
if(t1.joinable())
t1.join();
if(t2.joinable())
t2.join();
if(t3.joinable())
t3.join();
return 0;
}
2.3 future、promise
如果需要获得线程中的值,可以采用条件变量等待子线程结束,再打印想要的值
#include<stdio.h>#include<thread>#include<queue>#include<mutex>#include<string>#include<chrono>#include<iostream>#include<condition_variable>#include<future>usingnamespace std;
mutex mtx;
condition_variable cv;voidtask(int a,int b, promise<int>& ret){int ret_a = a * a;int ret_b = b *2;
ret = ret_a + ret_b;
cv.notify_one();}//这个版本导致代码太多,使用future可以优雅的解决intmain(){int ret =0;//如果需要传递引用,则需要加ref//将p传递进去
thread t(task,1,2,ref(ret));
unique_lock<mutex>lock(mtx);//等待线程通知,然后往下执行
cv.wait(lock);
cout<<"return value is"<< ret<<endl;if(t.joinable())
t.join();}
上述这种方式可以使用future更为优雅的解决
#include<stdio.h>#include<thread>#include<queue>#include<mutex>#include<string>#include<chrono>#include<iostream>#include<condition_variable>#include<future>usingnamespace std;voidtask(int a,int b, promise<int>& ret){int ret_a = a * a;int ret_b = b *2;
ret.set_value(ret_a+ret_b);}//这个版本导致代码太多,使用future可以优雅的解决intmain(){int ret =0;
promise<int>p;
future<int>f = p.get_future();//将f与p关联起来
thread t(task,1,2,ref(p));//f.get()会阻塞在这里等待 p给f传递值,且f.get()只能用一次
cout <<"return ret is :"<< f.get()<<endl;if(t.joinable())
t.join();}
如果使用多个子线程,但其中 f.get()只能只用一次,此时可以使用 share_future
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>
using namespace std;
void task(int a, future<int>&b, promise<int>& ret){
int ret_a = a * a;
//等待主线程的p_in 设置号值之后再继续
int ret_b = b.get()* 2;
ret.set_value(ret_a+ret_b);
}
//有时候 在给线程传值是,可能不需要立马传递值,可能需要过一段时间传递值
int main(){
int ret = 0;
promise<int>p_ret;
future<int>f_ret = p_ret.get_future();
//延时传值
promise<int>p_in;
future<int>f_in = p_in.get_future();
thread t(task,1,ref(f_in),ref(p_ret));
//do
p_in.set_value(8);
//
cout <<"return ret is :" << f_ret.get() <<endl;
if(t.joinable())
t.join();
}
2.4、功能优化
上诉方法可以通过系统函数写的更为简洁
#include<stdio.h>#include<thread>#include<queue>#include<mutex>#include<string>#include<chrono>#include<iostream>#include<condition_variable>#include<future>usingnamespace std;inttask(int a,int b){int ret_a = a * a;//等待主线程的p_in 设置号值之后再继续int ret_b = b *2;return ret_a+ret_b;}//为了简化上述方法,可以使用asyncintmain(){//使用async可以在线程中获得返回值//async不一定创建新的线程做计算,如果使用launch::async则会开启新的线程//launch::deferred 为延迟调用,当有fu.get()时,才会开启线程
future<int> fu =async(launch::async,task,1,2);
cout <<"return ret is :"<< fu.get()<<endl;}