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

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

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


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

生产·消费者模型示例

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 是圆的。


这样子可明白?

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