while 如何解决虚假唤醒 及 if 为什么就不行?

举报
看,未来 发表于 2021/10/24 16:44:05 2021/10/24
【摘要】 希望打开这篇能对你有所帮助。@[toc]这个问题其实第一次接触虚假唤醒就有答案了,但是当时太拽,留下张图啥也不讲明白,导致现在又不知道是为什么了。。。看了好久的网上的解说,也都是点到为止,哎,还有些写个“生产消费者”都写不明白,哎。 生产·消费者模型示例1、这段代码不是用来看懂的2、因为不了解生产·消费者模型,那怎么理解虚假唤醒啊?或者有没有掌握其它条件变量使用场景啊?3、这段代码只是用来回...

希望打开这篇能对你有所帮助。

@[toc]

这个问题其实第一次接触虚假唤醒就有答案了,但是当时太拽,留下张图啥也不讲明白,导致现在又不知道是为什么了。。。

看了好久的网上的解说,也都是点到为止,哎,还有些写个“生产消费者”都写不明白,哎。


在这里插入图片描述

生产·消费者模型示例

1、这段代码不是用来看懂的
2、因为不了解生产·消费者模型,那怎么理解虚假唤醒啊?或者有没有掌握其它条件变量使用场景啊?
3、这段代码只是用来回忆一下,加讲解。

#include <iostream>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
using namespace std;

int current = 0;  // producer运行加1,consumer运行减1
int buf[10];
int in = 0, out = 0;
int items = 0, spaces = 10;
bool flag;  // 标记线程结束运行
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t notfull = PTHREAD_COND_INITIALIZER;  // 缓冲区不满
pthread_cond_t notempty = PTHREAD_COND_INITIALIZER;  // 缓冲区不空

void *producer( void *arg ) {
    while( flag ) {
        pthread_mutex_lock( &mutex );  // 为保证条件变量不会因为多线程混乱,所以先加锁
        while( !spaces ) {  // 避免“惊群”效应,避免因其他线程实现得到事件而导致该线程“假醒”
            pthread_cond_wait( &notfull, &mutex );
        }
        buf[in] = current++;
        in = ( in + 1 ) % 10;
        items++;
        spaces--;

        printf( "producer %zu , current = %d\n", pthread_self(), current );
        for( int i = 0; i < 10; i++ ) {
            printf( "%-4d", buf[i] );
        }
        printf( "\n\n" );

        pthread_cond_signal( &notempty );
        pthread_mutex_unlock( &mutex );
    }
    pthread_exit( NULL );
}

void *consumer( void *arg ) {
    while( flag ) {
        pthread_mutex_lock( &mutex );
        while( !items ) {
            pthread_cond_wait( &notempty, &mutex );
        }
        buf[out] = -1;
        out = ( out + 1 ) % 10;
        current--;
        items--;
        spaces++;

        printf( "consumer %zu , current = %d\n", pthread_self(), current );
        for( int i = 0; i < 10; i++ ) {
            printf( "%-4d", buf[i] );
        }
        printf( "\n\n" );

        pthread_cond_signal( &notfull );
        pthread_mutex_unlock( &mutex );
    }
    pthread_exit( NULL );
}

int main() {
    memset( buf, -1, sizeof(buf) );
    flag = true;
    pthread_t pro[10], con[10];
    int i = 0;

    for( int i = 0; i < 10; i++ ) {
        pthread_create( &pro[i], NULL, producer, NULL );
        pthread_create( &con[i], NULL, consumer, NULL );
    }

    sleep(1);  // 让线程运行一秒
    flag = false;

    for( int i = 0; i < 10; i++ ) {
        pthread_join( pro[i], NULL );
        pthread_join( con[i], NULL );
    }

    return 0;
} 

使用 if 的代码流程

运行,运行,运行,三个线程都走到了 wait 这一步了。
为什么?因为条件变量进入了wait会释放锁啊。

现在都进来了哈。

这时候一个唤醒,肯定只有一个线程拿到了锁,因为锁只有一把,但是被唤醒的就不止是一个线程了。那没拿到锁的线程呢?
没拿到,就没拿到呗,继续往下呗,还想怎么样?

继续往下怎样?没有资源消费呗。脾气不好的线程就 core dump呗。
脾气好点呢?那你两次 unlock() 是没问题吗?

不过哈,这个虚假唤醒呐,没那么点背,触发概率不高,所以人家就懒的修复咯,性价比不高嘛,用户自己解决吧。


使用 while 的代码流程

运行,运行,运行,三个线程都走到了 wait 这一步了。
为什么?因为条件变量进入了wait会释放锁啊。

现在都进来了哈。

这时候,就算三个都给唤醒了,剩下那俩也得再兜回去继续趴着 wait 去。因为 while 是圆的。


这样子可明白?

对了,顺带说一句,条件变量的学名叫管程,别下次人家问管程还反问一句管程是啥,丢人。。。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。