希望打开这篇能对你有所帮助。
这个问题其实第一次接触虚假唤醒就有答案了,但是当时太拽,留下张图啥也不讲明白,导致现在又不知道是为什么了。。。
看了好久的网上的解说,也都是点到为止,哎,还有些写个“生产消费者”都写不明白,哎。
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( ¬full, &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( ¬empty );
pthread_mutex_unlock( &mutex );
}
pthread_exit( NULL );
}
void *consumer( void *arg ) {
while( flag ) {
pthread_mutex_lock( &mutex );
while( !items ) {
pthread_cond_wait( ¬empty, &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( ¬full );
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;
}
运行,运行,运行,三个线程都走到了 wait 这一步了。 为什么?因为条件变量进入了wait会释放锁啊。
现在都进来了哈。
这时候一个唤醒,肯定只有一个线程拿到了锁,因为锁只有一把,但是被唤醒的就不止是一个线程了。那没拿到锁的线程呢? 没拿到,就没拿到呗,继续往下呗,还想怎么样?
继续往下怎样?没有资源消费呗。脾气不好的线程就 core dump呗。 脾气好点呢?那你两次 unlock() 是没问题吗?
不过哈,这个虚假唤醒呐,没那么点背,触发概率不高,所以人家就懒的修复咯,性价比不高嘛,用户自己解决吧。
运行,运行,运行,三个线程都走到了 wait 这一步了。 为什么?因为条件变量进入了wait会释放锁啊。
现在都进来了哈。
这时候,就算三个都给唤醒了,剩下那俩也得再兜回去继续趴着 wait 去。因为 while 是圆的。
这样子可明白?
对了,顺带说一句,条件变量的学名叫管程,别下次人家问管程还反问一句管程是啥,丢人。。。