admin管理员组文章数量:1599892
1. 主动唤醒:
std::condition_variable
的notify_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
不会存在伪唤醒的情况,推荐使用这种方式!
本文标签: 变量条件conditionvariablestd
版权声明:本文标题:C++中条件变量std::condition_variable的唤醒说明 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728324064a1154203.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论