条件变量
上一章我们介绍了如何用互斥量给线程加锁,达到保证数据,操作完整性的目的,这一章我们会介绍一个具有类似功能的类模板,条件变量。
条件变量std::condition_variable;
它一般被用来阻塞一个或同时阻塞多个线程,直到另一个线程去通知它,它才放弃阻塞。它一般会和互斥量一起使用。
条件变量的使用
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
using namespace std;
class A{
public:
void Costomer(){
for(int i = 0;i < 1000;++i){
unique_lock<mutex> guard(my_mutex);
goods.push_back(i);
cout << "往物品栏里添加了一个编号为" << i << "的物品" << endl;
cond.notify_one(); //唤醒一个wait()
}
}
void Provider(){
while(true){
unique_lock<mutex> gurad(my_mutex);
//wait用来等待条件变量的反应
cond.wait(guard,[this]{ //条件为假则阻塞
if(!goods.empty()){
return true;
}else return false;
}
cout << "消费者买走物品,物品编号是: " << goods.front() << endl;
goosd.pop();
}
}
private:
//容器,用来表示商品栏
queue<int> MsgRecQue;
//定义一个互斥量
mutex my_mutex;
//定义一个条件变量
condition_variable cond;
};
int main(){
A myobj;
thread obj1(&A::Provider,&myobj); //第二个参数是引用可以保证两个线程使用的是同一个对象
thread obj2(&A::Costomer,&myobj);
obj1.join();
obj2.join();
return 0;
}
运行结果:
函数简析
notify_once():条件变量.notify_once(): 它用来唤醒一个wait,并检查它是否符合某种条件(wait的第二个参数)(有可能导致死锁,例如我们把上面的!goods.empty()改成goods,empty())
notify_all():条件变量.notify_all(): 它和上面用法类似,但它可以唤醒多个wait;
wait():条件变量.wait(mutex互斥量,【一个bool型返回值的函数对象(我用的lambda)】);【】为可选参数;
当其他线程用notifiy_once()将wait()【正在阻塞的状态】唤醒时,它会不断尝试获取互斥量锁,如果获取不到,它将继续阻塞在这里继续尝试获取锁,如果获取到了锁,它将继续执行下面的语句
wait() 的参数:
当它有两个参数的时候:
- 第二个参数返回true: wait将直接返回。并执行下面的语句
- 第二个参数返回false: wait将解锁互斥量,并堵塞到本行,等待条件为真
当它只有一个参数时,它的作用和第二个参数返回false类似;
其他函数
wait_for(); //阻塞当前线程,直到条件变量被唤醒或超时
wait_until(); //阻塞当前线程,直到条件变量被唤醒或到了一定时段
总结
条件变量一般和互斥量组合使用,互斥量一定要有unlock()函数,(尝试过用lock_gurad来做互斥量,但它显示没有重载该函数,估计就是因为这个原因);
它是一种线程同步原语,能保证操作和数据的一致性,用不好也会有死锁。
它相比单纯用互斥量可以减少每次provider轮询抢锁的花销(别人notify我才去尝试抢锁), 效率会有一定提升。