记一次由虚假唤醒产生的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. 【Linux】常用基本指令大汇总系列(篇一)【超详细的图解保姆教程】

    常用基本指令大汇总系列(篇一) 大家好,欢迎大家来到我的博客.从今天开始,博主就要开启一个全新的系列了!Linux操作系统常用基本指令汇总系列,当大家按顺序学完这个系列,在Linux上编写基本的C语言 ...

  2. PyTorch下,使用list放置模块,导致计算设备不一的报错

    报错 在复现 Transformer 代码的训练阶段时,发生报错: RuntimeError: Expected all tensors to be on the same device, but f ...

  3. Codeforces Round #887 (Div. 2) A-D

    比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; int a[507]; bool ...

  4. ARM 中SP,LR,PC寄存器的作用

    ARM中所有寄存器都是32位的.这里以cortex-a7内核的MX6ULL处理器为例,按照功能可以分为两类:运行需要寄存器(程序正常运行所需要的,比如变量暂存,pc制作等,总共43个),系统管理控制寄 ...

  5. SpringBoot使用git-commit-id-maven-plugin打包

    简介 git-commit-id-maven-plugin 是一个maven 插件,用来在打包的时候将git-commit 信息打进jar中. 这样做的好处是可以将发布的某版本和对应的代码关联起来,方 ...

  6. linux中cron表达式指南

    Cron是什么? 简单来讲,cron是基于Unix的系统上的一个实用程序.它使用户能够安排任务在指定的[日期/时间]定期运行.它自然是一个伟大的工具,可以自动运行大量流程,否则需要人工干预. Cron ...

  7. Spring源码之spring事务

    目录 Spring事务 事务自定义标签 自定义标签 解析标签 bean 的初始化 InfrastructureAdvisorAutoProxyCreator 获取增强方法 获取所有增强中内适用于当前方 ...

  8. SpringCloud SpringBoot 组件使用:SpringBoot Actuator

    基础篇 一.什么是Spring Actuator? spring-boot-starter-actuator模块是一个spring提供的监控模块.我们在开运行发过程中,需要实时和定时监控服务的各项状态 ...

  9. [BUUCTF][WEB][极客大挑战 2019]Http 1

    打开靶机提供的url 右键查看网页源代码 发现一个链接 (Secret.php),访问看看 返回: It doesn't come from 'https://Sycsecret.buuoj.cn' ...

  10. 商店销售预测(回归&随机森林)

    ​ 目录 一.题目概要 二.导入包和数据集 三.数据处理 四.描述性分析 五.探索性数据分析 六.模型一:线性回归 七.模型2:随机森林 一.题目概要 在Kaggle竞赛中,要求我们应用时间序列预测, ...