条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待 “条件变量的条件成立” 而挂起;另一个线程使 “条件成立”,然后唤醒另一个等待线程。
C++11提供了多线程线程同步的条件变量接口可以调用,一般都需要配合互斥量进行使用,常说到的生产者-消费者模型就可以用条件变量进行实现。
主要用到的结果接口有:
void wait(std::unique_lock<std::mutex>& lock);
//Predicate 谓词函数,第二个参数可为lambda表达式或函数
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
template< class Rep, class Period >
std::cv_status wait_for( std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time);
template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);
void notify_one() noexcept;
唤醒等待线程其中一个。
void notify_all() noexcept;
唤醒全部等待线程。
接下来给一个例子,有3个线程,1个线程“生产消息”,另外两个线程“等待唤醒”,当消息队列中有消息的时候,唤醒另外两个线程去拿消息。当消息队列中无消息时,另外两个线程挂起,“生产”线程开始生产消息。
#include
#include
#include
#include
#include
using namespace std;
class A{
public:
void inMsgRecvQueue(){
for(int i = 0; i < 100000; ++i){
printf("插入消息队列: %d, 线程id: %d\n", i, this_thread::get_id());
unique_lock<mutex> sbguard1(my_mutexl);
msgRecvQueue.push_back(i); //将消息加入消息队列
my_cond.notify_one(); //唤醒被wait的那个线程
/*
但此时被唤醒的线程不一定就处在wait, 他可能已经逃离wait并且
获取到消息, 在做一些处理了(且已经放锁, 不然本线程不可能拿到锁走到这)
那此时的notify_one就无效
*/
}
return;
}
void outMsgRecvQueue(){
while(true){
unique_lock<mutex> sbguardl(my_mutexl);
//wait()第二个参数返回值为false, wait将解锁, 然后阻塞在wait这一行
//直到其他某个线程调用notify_one()成员函数将该线程唤醒.
//如果没有第二个参数, 该线程也会阻塞在wait这一行, 等待唤醒
my_cond.wait(sbguardl, [this](){
if(!msgRecvQueue.empty()){
return true; //有信息就往下取, 否则就阻塞
}
return false;
});
//此时处于锁状态,消息队列中至少有1条数据
printf("从消息队列中取出: %d, 线程id: %d\n", msgRecvQueue.front(), this_thread::get_id());
msgRecvQueue.pop_front();
sbguardl.unlock(); //拿完数据,记得解锁
//下面可以对数据做一些处理......
}
}
void outMsgRecvQueue1(){
while(true){
unique_lock<mutex> sbguardl(my_mutexl);
//wait()第二个参数返回值为false, wait将解锁, 然后阻塞在wait这一行
//直到其他某个线程调用notify_one()成员函数将该线程唤醒.
//如果没有第二个参数, 该线程也会阻塞在wait这一行, 等待唤醒
my_cond.wait(sbguardl, [this](){
if(!msgRecvQueue.empty()){
return true; //有信息就往下取, 否则就阻塞
}
return false;
});
//此时处于锁状态,消息队列中至少有1条数据
printf("从消息队列中取出: %d, 线程id: %d\n", msgRecvQueue.front(), this_thread::get_id());
msgRecvQueue.pop_front();
sbguardl.unlock(); //拿完数据,记得解锁
//下面可以对数据做一些处理......
}
}
private:
list<int> msgRecvQueue;
mutex my_mutexl;
condition_variable my_cond;
};
int main(int argc, char const *argv[])
{
/*
condition_variable / wait() / notify_one()
线程A: 等待一个条件满足就唤醒B
线程B: 做一些业务
条件变量配合互斥量一起使用, 生产者-消费者
两个线程共用一把锁, 竞争取锁
wait会不断尝试拿锁, 拿到锁后就往下执行
如果wait有第二个参数,则由第二个参数返回值决定
false - 解锁,休眠,等待被唤醒 true - wait返回,继续往下走(可以通过lambda表达式判定)
*/
A myobj;
thread b(&A::inMsgRecvQueue, &myobj);
thread a(&A::outMsgRecvQueue, &myobj);
thread c(&A::outMsgRecvQueue1, &myobj);
a.join();
b.join();
c.join();
return 0;
}