使用条件变量时,仅仅从pthread_cond_wait返回就说条件成立是不恰当的。我们正确使用pthread_cond_wait的唯一方式是当线程被从pthread_cond_wait唤醒时,再检查一下我们等待的条件(pthread_cond_wait的返回使得我们再次获得mutex,这时我们可以讲,我们拥有绝对的对等待条件的访问权,我们对其状态拥有绝对自信)

我看到有人把POSIX对pthread_cond_signal实现的约束(详见apue page334)作为从pthread_cond_wait返回必须进行变量值检查的原因,这不够准确,根本原因只有一个,只有当前线程获取到了被保护变量的mutex才能对被保护变量的值做推论。

为了更好的说明问题,假设有这样一个应用场景:我们使用线程A,B来消费一个值,线程C用于生产这个值,生产后就广播A与B,主线程等待他们。

 #include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<assert.h>
#include<signal.h> pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; volatile int value = ; pthread_t tid;
pthread_t tid2;
pthread_t tid3; void* thread_func(void* arg) {
pthread_mutex_lock(&mutex); if ( == value) {
pthread_cond_wait(&cond, &mutex);
}
printf("the thread_func is finished value = %d\n", value);
value = ;
pthread_mutex_unlock(&mutex);
} void* thread_func3(void* arg) {
pthread_mutex_lock(&mutex); if ( == value) {
pthread_cond_wait(&cond, &mutex);
}
printf("the thread_func3 is finished value = %d\n", value);
value = ;
pthread_mutex_unlock(&mutex);
} void* thread_func2(void* arg) {
pthread_mutex_lock(&mutex);
value = ;
pthread_cond_broadcast(&cond);
printf("the thread_func2 is finished value = %d\n", value);
pthread_mutex_unlock(&mutex);
} int
main(int argc, char* argv[])
{
// pthread_attr_t attr;
// assert(0 == pthread_attr_init(&attr));
// pthread_create(&tid2, NULL, thread_func2, NULL);
// pthread_join(tid2, NULL);
// printf("thread id = %ld\n", tid);
// printf("thread2 id = %ld\n", tid2);
// printf("thread3 id = %ld\n", tid3);
// printf("---------keep going---------\n");
// pthread_create(&tid, NULL, thread_func, NULL);
// pthread_create(&tid3, NULL, thread_func3, NULL);
// pthread_join(tid, NULL);
// pthread_join(tid3, NULL);
// pthread_cond_destroy(&cond); pthread_attr_t attr;
assert( == pthread_attr_init(&attr));
pthread_create(&tid, NULL, thread_func, NULL);
pthread_create(&tid3, NULL, thread_func3, NULL);
sleep();
printf("thread id = %ld\n", tid);
printf("thread3 id = %ld\n", tid3);
printf("make sure the thread and thread3 is created\n"); pthread_create(&tid2, NULL, thread_func2, NULL);
pthread_join(tid2, NULL);
pthread_join(tid, NULL);
pthread_join(tid3, NULL);
pthread_cond_destroy(&cond);
pthread_attr_destroy(&attr); }

程序中需要注意line70,为了使得pthread_cond_broadcast成功,这里被动等待了5s,其它的都没啥说的。

我们假设thread_func收到广播后先执行,thread_func持有mutex,所以thread_func3在从pthread_cond_wait这里返回就是失败的,虽然收到了广播,但是它压根拿不到mutex。

等到thread_fucnc释放mutex后,thread_func3开始执行,这时value的值已经不是thread_func3被通知时的值了(这个例子比较极端)。所以我们在从pthread_cond_wait返回后需要再验证我们等待的value是否是期望的值。

那我们再进一步,thread_func3从pthread_cond_wait返回后再加个if判断不就行了吗???代码如下:

...
//in the thread_func3
if( == value) {
pthread_cond_wait();
}
if( == value) {
pthread_cond_wait();
}
...

那如果又发生了thread_func抢先消费掉value的情况呢?是不是又需要判断一次value,如此往复,当然写成这样才是唯一解了:

while( == value) {
pthread_cond_wait();
}

好了,谢谢各位的阅读。恳请批评指正!

聊聊pthread_cond_wait的虚假唤醒的更多相关文章

  1. pthread_cond_wait虚假唤醒

    pthread_cond_wait中的while()不仅仅在等待条件变量前检查条件cond_is_false是否成立,实际上在等待条件变量后也检查条件cond_is_false是否成立.在多线程等待的 ...

  2. 刨根问底系列(1)——虚假唤醒(spurious wakeups)的原因以及在pthread_cond_wait、pthread_cond_singal中使用while的必要性

    刨根问底之虚假唤醒 1. 概要 将会以下方式展开介绍: 什么是虚假唤醒 什么原因会导致虚假唤醒(两种原因) 为什么系统内核不从根本上解决虚假唤醒这个"bug"(两个原因) 开发者如 ...

  3. 多线程编程中条件变量和的spurious wakeup 虚假唤醒

    1. 概述 条件变量(condition variable)是利用共享的变量进行线程之间同步的一种机制.典型的场景包括生产者-消费者模型,线程池实现等. 对条件变量的使用包括两个动作: 1) 线程等待 ...

  4. 什么是虚假唤醒 spurious wakeup

    解释一下什么是虚假唤醒? 说具体的例子,比较容易说通. pthread_mutex_t lock; pthread_cond_t notempty; pthread_cond_t notfull; v ...

  5. 【转】pthread_cond_signal 虚假唤醒问题

    引用:http://blog.csdn.net/leeds1993/article/details/52738845 什么是虚假唤醒? 举个例子,我们现在有一个生产者-消费者队列和三个线程. I.1号 ...

  6. notify丢失、虚假唤醒

    notify丢失: 假设线程A因为某种条件在条件队列中等待,同时线程B因为另外一种条件在同一个条件队列中等待,也就是说线程A/B都被同一个Object.wait()挂起,但是等待的条件不同. 现在假设 ...

  7. JUC虚假唤醒(六)

    为什么条件锁会产生虚假唤醒现象(spurious wakeup)? ​ 在不同的语言,甚至不同的操作系统上,条件锁都会产生虚假唤醒现象.所有语言的条件锁库都推荐用户把wait()放进循环里: whil ...

  8. (三)juc高级特性——虚假唤醒 / Condition / 按序交替 / ReadWriteLock / 线程八锁

    8. 生产者消费者案例-虚假唤醒 参考下面生产者消费者案例: /* * 生产者和消费者案例 */ public class TestProductorAndConsumer { public stat ...

  9. Java-JUC(八):使用wait,notify|notifyAll完成生产者消费者通信,虚假唤醒(Spurious Wakeups)问题出现场景,及问题解决方案。

    模拟通过线程实现消费者和订阅者模式: 首先,定义一个店员:店员包含进货.卖货方法:其次,定义一个生产者,生产者负责给店员生产产品:再者,定义一个消费者,消费者负责从店员那里消费产品. 店员: /** ...

随机推荐

  1. Hibernate中的主键生成器generator

    本文讲述Hibernate的generator属性的意义.Generator属性有7种class,本文简略描述了这7种class的意义和用法. [xhtml] view plaincopy <c ...

  2. hibernate中Query的list和iterator区别(续)

    打开cache后query的list和iterator方法区别 将query 的cache打开的话,缓存的是query本身,以hql 生成的 sql ,再加上参数,分页等信息做为key值,而不是que ...

  3. 02-windows 安装以太坊 ethereum 客户端 (win7-64)-大叔思维

    以太坊(Ethereum)是一个运行智能合约的去中心化平台(Platform for Smart Contract),平台上的应用按程序设定运行,不存在停机.审查.欺诈.第三方人为干预的可能.以太坊平 ...

  4. Bootstrap框架的了解和使用之栅格系统

       前    言 Bootstrap Bootstrap 包含了一个响应式的.移动设备优先的.不固定的网格系统,可以随着设备或视口大小的增加而适当地扩展到 12 列.它包含了用于简单的布局选项的预定 ...

  5. 基于 Electron 的爬虫框架 Nightmare

    作者:William 本文为原创文章,转载请注明作者及出处 Electron 可以让你使用纯 JavaScript 调用 Chrome 丰富的原生的接口来创造桌面应用.你可以把它看作一个专注于桌面应用 ...

  6. oracle sql*plus常用命令

    一.sys用户和system用户Oracle安装会自动的生成sys用户和system用户(1).sys用户是超级用户,具有最高权限,具有sysdba角色,有create database的权限,该用户 ...

  7. 多线程进阶---Thread.join()/CountDownLatch.await() /CyclicBarrier.await()

    Thread.join() CountDownLatch.await() CyclicBarrier.await() 三者都是用来控制程序的"流动" 可以让程序"堵塞&q ...

  8. 【OOM】GC overhead limit exceeded

    我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...

  9. 实际开发--->php时间函数

    当前日期(例:2017-10-04):date('Y-m-d',time()); 当前时间戳:strtotime(date('Y-m-d H-i-s',time()); 当前年月(例:2017-10) ...

  10. hdu2222 ac自动机入门

    Keywords Search Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...