一:使用条件变量condition_variable等待条件成立
class A
{
public:
int i;
A(int i) : i(i){}
};
mt19937 rnd; //生成随机数
queue<A> q;
mutex m;
condition_variable condition;
void push_data()
{
this_thread::sleep_for(100ms);
int i;
for (i = 0; i < 10; i++) //压入10条数据
{
A data(int(rnd()) % 1000);
//大括号添加作用域, 使退出大括号时, lock_guard能析构
{
lock_guard lock1(m);
q.push(data);
}
printf("push data:%d\n", data.i);
condition.notify_one(); //当process线程在wait等待阻塞时, 唤醒甲去查验条件
}
}
void process()
{
while (true) //让其一直运行,
{
unique_lock lock1(m);
//condition.wait(): 条件(第二个参数)成立(本例为lambda条件)则返回, 且锁住互斥, 不成立则阻塞或等待同时释放互斥
condition.wait(lock1, []
{
return !q.empty();
});
A data = q.front();
q.pop();
this_thread::sleep_for(100ms);
printf("process data:%d\n", data.i);//模拟处理数据
}
}
int main()
{
thread t1(process); //处理线程(消费者)
thread t2(push_data);//生产线程
t1.join();
t2.join();
return 0;
}
某次的运行结果
push data:-684
push data:302
process data:-684
process data:302
push data:-562
push data:-711
process data:-562
process data:-711
push data:204
process data:204
//此时程序还没停止, push_data线程不再压入数据, 但process线程仍在等待数据
条件变量实际上是忙等(busy-wait)的优化, 忙等就是一直循环等待条件成立, 浪费cpu资源
由于在wait的调用期间, 条件(第二个参数)可能会被多次查验, 如果产生true则立刻返回,
若此函数的调用产生副作用, 则会造成数量和频率都不确定的伪唤醒
伪唤醒: process线程获得互斥, 但却不是直接响应了push_data线程的结果
例如process线程的lamdba条件里为队列q添加数据
bool predicate()
{
q.push(1);
return true;
}
void process()
{
while (true) //让其一直运行,
{
unique_lock lock1(m);
//condition.wait(): 条件(第二个参数)成立(本例为lambda条件)则返回, 解锁互斥, 不成立则阻塞或等待
//此时无法得知predicate会不会造成副作用,
//但实际上predicate修改了队列q并直接返回true
condition.wait(lock1, predicate);
A data = q.front();
q.pop();
this_thread::sleep_for(100ms);
printf("process data:%d\n", data.i);//模拟处理数据
}
}
会一直输出1, push_data线程的数据没发挥作用
process data:1
process data:1
process data:1
process data:1
//无限重复
因此利用condition_variable时尤其要小心条件的验证不能产生副作用
当wait进入阻塞时, 才需要notify_one唤醒, 若条件直接成立, 则不会阻塞, 无须唤醒
当进入阻塞时, norify_one并不会直接唤醒, 仍需要检查条件是否成立
void process(int i)
{
while (true) //让其一直运行,
{
unique_lock lock1(m);
//此时上面push_data的唤醒不起作用,因为notify_one后还需检查条件, 此处条件不成立
condition.wait(lock1, []{return false;});
A data = q.front();
q.pop();
printf("thread:%d, process data:%d\n", i, data.i);//模拟处理数据
this_thread::sleep_for(100ms);
}
}
push data:-684
push data:302
push data:-562
push data