条件变量—虚假唤醒(放到while循环的原因)

**Linux中帮助中提到:**在多核处理器下,pthread_cond_signal可能会激活多于一个线程(阻塞在条件变量上的线程)。结果是,当一个线程调用pthread_cond_signal()后,多个调用pthread_cond_wait()或pthread_cond_timedwait()的线程返回。这种效应成为”虚假唤醒。虽然虚假唤醒在pthread_cond_wait函数中可以解决,为了发生概率很低的情况而降低边缘条件,效率是不值得的,纠正这个问题会降低对所有基于它的所有更高级的同步操作的并发度。所以pthread_cond_wait的实现上没有去解决它。添加while检查的做法被认为是增加了程序的健壮性,在IEEE Std 1003.1-2001中认为spurious wakeup是允许的。

所以通常的标准解决办法是这样的:
将条件的判断从if 改为while,举例子说明:
下面为线程处理函数:

static void *thread_func(void *arg)
{
    while (1) {
    pthread_mutex_lock(&mtx);           //这个mutex主要是用来保证pthread_cond_wait的并发性
    while (msg_list.empty())   {     //pthread_cond_wait里的线程可能会被意外唤醒(虚假唤醒),如果这个时候,则不是我们想要的情况。这个时候,应该让线程继续进入pthread_cond_wait
        pthread_cond_wait(&cond, &mtx);
    }
        msg = msg_list.pop();
        pthread_mutex_unlock(&mtx);             //临界区数据操作完毕,释放互斥锁
        // handle msg
    }
    return 0;
}

如果不存在虚假唤醒的情况,那么下面代码:

 while (msg_list.empty())   {
        pthread_cond_wait(&cond, &mtx);
    }

可以为

if (msg_list.empty())   {
        pthread_cond_wait(&cond, &mtx);
    }

但是存在虚假唤醒的时候,如果用if,而不用while,那么但被虚假唤醒的时候,不会再次while判断,而是继续下面执行msg = msg_list.pop();这其实是逻辑上有问题的。因为下面的代码已经假定了msg_list不是空的。

举例:
多核环境下,线程1、2都在thread_func函数中阻塞等待,某刻这两个线程被pthread_cond_signal同时唤醒并竞争互斥锁想要向下运行,线程1成功获取锁,并向下运行从消息队列取出消息,此刻线程2仍因为获取不到锁而阻塞,线程1取完消息后释放锁后,线程2继续向下运行,但此时消息队列中已没有数据,所以对于线程2来说,此次唤醒是虚假唤醒。需要在被唤醒后在次判断条件是否成立来避免这种情况

注:调用pthread_cond_broadcast会唤醒所有等待线程,也会出现虚拟唤醒情况,同多核处理器的情况一样,所以调用pthread_cond_broadcast的程序中也需要做虚拟唤醒处理
举例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int flag = 1;

void *thread(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		while(flag != 0)
		{
			printf("thread %d: wait\n", (int)arg);
			pthread_cond_wait(&cond, &mutex);
			printf("thread %d:get cond, %d\n", (int)arg, flag);
			flag = 0;
			printf("thread %d: end\n", (int)arg);
		}	
		pthread_mutex_unlock(&mutex);
	}
	pthread_exit(0);
	
}

int main()
{
	int i;
	pthread_t id;
	
	pthread_create(&id, NULL, thread, (void *)0);
	pthread_create(&id, NULL, thread, (void *)1);
	sleep(3);
	printf("broadcast\n");
	pthread_cond_broadcast(&cond);
	while(1)
		;
	return 0;
}

运行结果:主线程调用pthread_cond_broadcast后,线程1比线程0先唤醒,线程1在pthread_cond_wait函数中成功加锁,此时线程0虽唤醒但阻塞在pthread_cond_wait中的加锁操作,线程1修改完flag值后释放锁,线程0拿到锁继续执行,但flag值已被线程1更改。所以这次唤醒对于线程0来说是虚假唤醒。

thread 1: wait
thread 0: wait
broadcast
thread 1:get cond, 1
thread 1: end
thread 0:get cond, 0
thread 0: end

参考: https://blog.csdn.net/ysu108/article/details/49508205

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值