博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
条件变量之虚假唤醒
阅读量:4072 次
发布时间:2019-05-25

本文共 2761 字,大约阅读时间需要 9 分钟。

如有错误请及时指正!

文章目录

引言

当我们使用互斥量(Mutex)条件变量(condition_variable)进行多线程同步时有可能会产生虚假唤醒现象, 那么究竟什么是虚假唤醒,它会在什么情况下被触发,什么情况下无需考虑虚假唤醒,如何避免?

1.什么是虚假唤醒?

Linux帮助中有提到

在多核处理器下,pthread_cond_signal可能会激活多于一个线程(阻塞在条件变量上的线程)。结果是,当一个线程调用pthread_cond_signal()后,多个调用pthread_cond_wait()或pthread_cond_timedwait()的线程返回。这种效应成为”虚假唤醒”(spurious

wakeup)

通俗点的解释就是:

你(消费线程)收到了其他线程(生产)传来的唤醒信号,但是你唤醒后发现别的消费线程处理的比你快,此时没有数据被可以用于操作,这种情况的发生是预期之外的,称之为虚假唤醒。

2.什么情况下会发生虚假唤醒

以以下C++代码举例。

#include 
#include
#include
#include
#include
#include
#include
using namespace std;#define PRODUCTER_NUM 10#define CONSUMER_NUM 100std::mutex mutexForProduct;static queue
queueForProduct;unsigned uProductTotalNum = 0;condition_variable condVal;//生产线程void ConsumerFunction(unsigned threadIndex){ while(1) { unique_lock
locker(mutexForProduct); if(queueForProduct.empty()) { condVal.wait(locker); } if(queueForProduct.empty()) { printf("Consumer[%02u] Want To Buy Product, But There is not remain Product", threadIndex); assert(false); } unsigned productIndex = queueForProduct.front(); printf("Consumer[%02u] Buying Product[%06u], RemainSize= %lu\n", threadIndex, productIndex, queueForProduct.size()); queueForProduct.pop(); }}//消费线程void ProducerFunction(){ while(1) { usleep(100 * 1000); unique_lock
locker(mutexForProduct); queueForProduct.push(++uProductTotalNum); condVal.notify_one(); }}int main(){ for(unsigned i = 0; i < CONSUMER_NUM; i++) thread(&ConsumerFunction, i).detach(); for(unsigned i = 0; i < PRODUCTER_NUM; i++) thread(&ProducerFunction).detach(); sleep(3600); return 0;}
  • 当消费者的数量(CONSUMER_NUM)为1时没有其他线程竞争队列,不会触发虚假唤醒。
  • 当生产者的数量(PRODUCTER_NUM)为1时,
    • 当使用notify_one通知消费线程时,不会发生虚假唤醒,因为每次只会有一个消费者线程收到信号被唤醒,在产品被消耗掉之前不会有新的信号发出来。
    • 当使用notify_all通知消费线程时,会发生虚假唤醒,会有多个消费者线程收到信号被唤醒,当一个线程被唤醒之前,可能其他线程先被唤醒先持有锁,将产品消耗掉。
  • 当生产者的数量(PRODUCTER_NUM)大于1时,无论是使用notify_one,或者是notify_all都会发生虚假唤醒,当多个生产者使用notify_one时,多个线程被唤醒,有可能其中一个处理的特别快,将所有的数据都处理完毕,那么接下来被唤醒的线程都无数据可处理

所以得出以下结论:

结论

只有在生产线程与消费线程都为多个的情况下才有可能发生虚假唤醒,只要有一方的数量为1,就不会发生!

3.如何避免虚假唤醒

将消费者线程修改如下

void ConsumerFunction(unsigned threadIndex){
while(1) {
unique_lock
locker(mutexForProduct); while(queueForProduct.empty()) {
condVal.wait(locker); } if(queueForProduct.empty()) {
printf("Consumer[%02u] Want To Buy Product, But There is not remain Product", threadIndex); assert(false); } unsigned productIndex = queueForProduct.front(); printf("Consumer[%02u] Buying Product[%06u], RemainSize= %lu\n", threadIndex, productIndex, queueForProduct.size()); queueForProduct.pop(); }}

被唤醒后再次判断,若无数据可处理,则应继续休眠。


作者:shizheng163

来源:CSDN
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!


你可能感兴趣的文章
备忘:java中的递归
查看>>
Solr及Spring-Data-Solr入门学习
查看>>
python_time模块
查看>>
python_configparser(解析ini)
查看>>
selenium学习资料
查看>>
<转>文档视图指针互获
查看>>
从mysql中 导出/导入表及数据
查看>>
HQL语句大全(转)
查看>>
几个常用的Javascript字符串处理函数 spilt(),join(),substring()和indexof()
查看>>
javascript传参字符串 与引号的嵌套调用
查看>>
swiper插件的的使用
查看>>
layui插件的使用
查看>>
JS牛客网编译环境的使用
查看>>
9、VUE面经
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>