记一次由虚假唤醒产生的bug

int a代表产品数量最少0最多10,有两个生产者,三个消费者,用多线程和条件变量模拟生产消费过程:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
//产品0-10
int a = 0;
//条件变量,互斥变量
pthread_cond_t cond_produce, cond_consume;
pthread_mutex_t mutex; //生产过程
void *produce(void *argv)
{
while(1)
{
pthread_mutex_lock(&mutex);
if(a >= 10)
pthread_cond_wait(&cond_produce, &mutex);
++a;
printf("%s生产后:%d\n",(char*)argv, a);
pthread_cond_signal(&cond_consume);
pthread_mutex_unlock(&mutex); }
return NULL;
} //消费过程
void *consume(void *argv)
{
while(1)
{
pthread_mutex_lock(&mutex); if(a <= 0)
pthread_cond_wait(&cond_consume, &mutex);
// 消费产品
--a;
printf("%s消费后:%d\n",(char*)argv, a);
//唤醒生产者
pthread_cond_signal(&cond_produce); pthread_mutex_unlock(&mutex);
}
return NULL;
} //初始化两个生产者和三个消费者
void initiate(pthread_t *producer1, pthread_t *producer2, pthread_t *customer1,
pthread_t *customer2, pthread_t *customer3)
{
pthread_cond_init(&cond_produce, NULL);
pthread_cond_init(&cond_consume, NULL);
pthread_mutex_init(&mutex, NULL);
//两个生产者,三个消费者
pthread_create(producer1, NULL, produce, "生产者1");
pthread_create(producer2, NULL, produce, "生产者2");
pthread_create(customer1, NULL, consume, "消费者1");
pthread_create(customer2, NULL, consume, "消费者2");
pthread_create(customer3, NULL, consume, "消费者3");
pthread_detach(*producer1);
pthread_detach(*producer2);
pthread_detach(*customer1);
pthread_detach(*customer2);
pthread_detach(*customer3); } int main() {
pthread_t producer1 = 0, producer2 = 0, customer1 = 0,
customer2 = 0, customer3 = 0;
initiate(&producer1, &producer2, &customer1, &customer2, &customer3); getchar();
pthread_cond_destroy(&cond_consume);
pthread_cond_destroy(&cond_produce);
pthread_mutex_destroy(&mutex);
return 0;
}

运行结果:

·····

生产者2生产后:11

消费者1消费后:10

消费者1消费后:9

消费者1消费后:8

消费者1消费后:7

消费者1消费后:6

消费者1消费后:5

消费者1消费后:4

消费者1消费后:3

消费者1消费后:2

消费者1消费后:1

消费者1消费后:0

消费者2消费后:-1

发现产品a会超过10也会小于0,刚开始以为pthread_cond_signal()会产生惊群效应,但后来实验了一下并不会,再查资料就知道了虚假唤醒

维基百科翻译:

当线程从等待已发出信号的条件变量中唤醒时,就会发生虚假唤醒,但发现它正在等待的条件未得到满足。 它被称为虚假的,因为线程似乎无缘无故地被唤醒了.但虚假的唤醒不会无缘无故地发生:它们通常发生是因为,在条件变量发出信号的时间和等待线程最终运行的时间之间,另一个线程运行并更改了条件.线程之间存在争用条件,典型的结果是,有时,在条件变量上唤醒的线程首先运行,赢得比赛,有时它运行第二,输掉比赛。

在许多系统上,特别是多处理器系统上,虚假唤醒的问题会加剧,因为如果在发出信号时有多个线程等待条件变量,系统可能会决定将它们全部唤醒,将每个唤醒一个线程视为唤醒所有线程,从而打破了信号和唤醒之间任何可能预期的1:1关系。 如果有十个线程在等待,则只有一个线程获胜,其他九个线程将经历虚假唤醒。

记一次由虚假唤醒产生的bug的更多相关文章

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

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

  2. notify丢失、虚假唤醒

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

  3. pthread_cond_wait虚假唤醒

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

  4. JUC虚假唤醒(六)

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

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

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

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

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

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

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

  8. java多线程 生产者消费者案例-虚假唤醒

    package com.java.juc; public class TestProductAndConsumer { public static void main(String[] args) { ...

  9. 什么是虚假唤醒 spurious wakeup

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

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

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

随机推荐

  1. 小知识:如何修改TFA下的OSW数据保留时间

    在Oracle社区可以搜索到这样的问题: How to change oswatcher retention when running under TFA 但很遗憾该问题目前也没有给出确切答复. 其实 ...

  2. nginx做白名单和限流

    ​ 在我们生产环境中使用到了地图服务,每个月有免费请求次数,近一个月请求次数突然暴涨,导致直接开启付费模式,一个月上百刀的花销着实难扛,根据实际我们的业务使用情况,远达不到付费标准,故考虑做白名单和限 ...

  3. NC20154 [JSOI2007]建筑抢修

    题目链接 题目 题目描述 小刚在玩JSOI提供的一个称之为"建筑抢修"的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者.但是T部落的基地里已经有N个建筑设施受到了严 ...

  4. Keil MDK STM32系列(十) Ubuntu下的PlatformIO开发环境

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  5. 【OpenGL ES】EGL+FBO离屏渲染

    1 前言 ​ FBO离屏渲染 中使用 GLSurfaceView 来驱动 Renderer 渲染图片,为了隐藏 GLSurfaceView,将其设置为透明的,并且宽高都设置为1.本文将使用 EGL 代 ...

  6. 【OpenGL ES】凸镜贴图

    1 前言 ​ 正方形图片贴到圆形上 中将正方形图片上的纹理映射到圆形模型上,同理,也可以将圆形上的纹理映射到凸镜的球形曲面上.如下图,最左边的竖条是原图片的截面(纹理坐标),最右边的竖条是变换后的顶点 ...

  7. Ubuntu如何卸载mysql

    首先在终端中查看MySQL的依赖项:dpkg --list|grep mysql 卸载: sudo apt-get remove mysql-common 卸载:sudo apt-get autore ...

  8. 【Azure 环境】Azure Key Vault 采用自签名证书,是否需要CA provider

    关于 Azure Key Vault 证书,密钥保管库证书支持适用于 x509 证书管理,它提供以下行为: 允许证书所有者通过密钥保管库创建过程或通过导入现有证书来创建证书. 包括自签名证书和证书颁发 ...

  9. kotlin协程异常处理之-try catch

    kotlin协程小记 协程的async使用 kotlin协程异常处理之-try catch kotlin协程异常处理之-CoroutineExceptionHandler 一.try catch tr ...

  10. mysql-批量修改表的主键id,修改成联合主键

    1.sql脚本 一. 通过sql脚本,查出所有表的功能,并编写插入修改的联合主键,sql select concat('ALTER table ', TABLE_NAME, ' DROP PRIMAR ...