C++中条件变量std::condition_variable的唤醒说明

1. 主动唤醒:

std::condition_variablenotify_one()以及notify_all()只是用于唤醒被wait...()函数阻塞的线程,假如wait...()函数没有被阻塞,比如wait(lock, func)中的func函数始终返回true,则wait函数就始终不会被阻塞,则根本就不需要唤醒。


2. 伪唤醒(spurious wakeup):

当使用的wait函数中没有谓词时,即不需要传入一个可调用对象的wait版本,此时要注意伪唤醒

所谓的伪唤醒不是说没收到notify...类函数的唤醒,而是说收到唤醒消息后,wait线程被唤醒,但是此时条件仍然不满足。例如:

std::vector<int> vi;
std::mutex mt;
std::condition_variable cv;
......
std::unique_lock lk(mt);
if (vi.empty())
{
	cv.wait(lk); // 1
}
......

上述代码是判断当vi为空的时候,就阻塞线程,直到另一个线程给vi塞入数据并使用notify...函数唤醒。然而,由于此种wait被唤醒后,在其重新获取锁lk之前(所有wait...唤醒后都会重新获取锁,然后再做其他事情),线程切换,另一个线程可能获取了锁并将vi里的数据清空了。此时线程再切换回来,使得上述代码处的wait成功获得了锁,然后wait返回,程序就继续执行wait后面的操作了,这就导致vi仍然为空时,还是执行了后面的操作,可能导致程序崩溃。

这里有两种方式能解决这个问题:
(1). 将非谓词wait加入循环中:
例如:

std::vector<int> vi;
std::mutex mt;
std::condition_variable cv;
......
std::unique_lock lk(mt);
while (vi.empty())
{
	cv.wait(lk); // 1
}
......

这样,即使1处的wait伪唤醒了,但是程序会通过while循环重新来判断条件,如果条件不适合,则继续执行wait并阻塞,直到wait被唤醒且条件符合为止。

(2). 使用带谓词的wait:
例子:

std::vector<int> vi;
std::mutex mt;
std::condition_variable cv;
......
std::unique_lock lk(mt);

cv.wait(lk, !vi.empty()); // 1
......

使用带谓词的wait后,当其他线程发出notify...消息后,wait会唤醒,然后:

(1). 先获取锁lk,成功得到锁就进行下一步,未得到锁则持续等待锁,直到得到锁;
(2).执行谓词。如果谓词返回true,则wait函数返回,程序继续执行其他操作;如果谓词返回false,则wait重新释放锁lk,并重新进入阻塞状态。

这种wait 不会存在伪唤醒的情况,推荐使用这种方式!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值