当前位置: 首页 > news >正文

《OSTEP》条件变量(chap30)

〇、前言

本文是对《OSTEP》第三十章的实践与总结。

一、条件变量

#include <pthread.h>
#include <stdio.h>
#include <assert.h>int buffer;
int count = 0; // 资源为空// 生产,在 buffer 中放入一个值
void put(int value) {assert(count == 0);count = 1;buffer = value;
}
// 消费,取出 buffer 中的值
int get() {assert(count == 1);count = 0;return buffer;
}/***********第一版本**********/
// 生产者
void *producer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {put(i);}return NULL;
}
// 消费者
void *consumer(void *arg) {while (1) {int temp = get();printf("消费的值:%d\n", temp);}return NULL;
}int main() {pthread_t p1, p2;int arg = 100;pthread_create(&p1, NULL, producer, &arg);pthread_create(&p2, NULL, consumer, NULL);// 等待两个线程结束pthread_join(p1, NULL);pthread_join(p2, NULL);return 0;
}

运行:

*** chap30_条件变量 % gcc -o a con_prodece.c
*** chap30_条件变量 % ./a
Assertion failed: (count == 0), function put, file con_prodece.c, line 10.
消费的值:0
Assertion failed: (count == 1), function get, file con_prodece.c, line 16.
zsh: abort      ./a

可以看到,断言直接失败。

pthread_cond_t cond;
pthread_mutex_t mutex;
/***********第二版本**********/
// 生产者
void *producer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);if (count == 1) {pthread_cond_wait(&cond, &mutex);}put(i);pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond);}return NULL;
}
// 消费者
void *consumer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);if (count == 0) {pthread_cond_wait(&cond, &mutex);}int temp = get();pthread_mutex_unlock(&mutex);printf("消费:%d\n", temp);pthread_cond_signal(&cond); }return NULL;
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t p1, p2;int arg = 100;pthread_create(&p1, NULL, producer, &arg);pthread_create(&p2, NULL, consumer, &arg);// 等待两个线程结束pthread_join(p1, NULL);pthread_join(p2, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

运行结果:

*** chap30_条件变量 % ./a                    
消费:0
消费:1
消费:2
消费:3
...
消费:95
消费:96
消费:97
消费:98
消费:99

可以看到,在两个线程的情况下,工作的很好。

/***********第三版本**********/
// 生产者
void *producer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);if (count == 1) {pthread_cond_wait(&cond, &mutex);}put(i);pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond);}return NULL;
}
// 消费者
void *consumer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);if (count == 0) {pthread_cond_wait(&cond, &mutex);}int temp = get();pthread_mutex_unlock(&mutex);printf("消费:%d\n", temp);pthread_cond_signal(&cond); }return NULL;
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t p1, p2, p3;int arg = 100;int arg1 = 50;int arg2 = 50;pthread_create(&p1, NULL, producer, &arg);pthread_create(&p2, NULL, consumer, &arg1);pthread_create(&p3, NULL, consumer, &arg2);// 等待两个线程结束pthread_join(p1, NULL);pthread_join(p2, NULL);pthread_join(p3, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

运行结果:

*** chap30_条件变量 % gcc -o a con_prodece2.c
*** chap30_条件变量 % ./a                    
消费:0
消费:1
Assertion failed: (count == 1), function get, file con_prodece2.c, line 18.
zsh: abort      ./a

可以看到,再增加了一个消费线程之后,出现了断言错误。这是因为出现了假唤醒,使得某个线程醒来后,断言错误。解决方法很简单,直接将 if()换成 while():

/***********第三版本**********/
// 解决虚假唤醒
// 生产者
void *producer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);while (count == 1) {pthread_cond_wait(&cond, &mutex);}put(i);pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond);}return NULL;
}
// 消费者
void *consumer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);while (count == 0) {pthread_cond_wait(&cond, &mutex);}int temp = get();pthread_mutex_unlock(&mutex);printf("消费:%d\n", temp);pthread_cond_signal(&cond); // 在解锁之后发出信号}return NULL;
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t p1, p2, p3;int arg = 100;int arg1 = 50;int arg2 = 50;pthread_create(&p1, NULL, producer, &arg);pthread_create(&p2, NULL, consumer, &arg1);pthread_create(&p3, NULL, consumer, &arg2);// 等待两个线程结束pthread_join(p1, NULL);pthread_join(p2, NULL);pthread_join(p3, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

运行结果:

luliang@shenjian chap30_条件变量 % ./a
消费:0
消费:2
消费:1
消费:3
消费:4
消费:5
...

可以看到会卡住,这其实是由于三个线程都睡眠导致的,这种情况是怎么发生的呢?
假设生产者唤醒了第一个消费者,消费者又恰巧唤醒了第二个生产者,第二个生产者被唤醒之后又睡眠。这样三个线程都在睡眠。解决问题的办法就是消费者只能唤醒生产者,生产者只能唤醒消费者。以下就是终极版本的代码:

#include <assert.h>
#include <pthread.h>
#include <stdio.h>int buffer;
int count = 0; // 资源为空
pthread_cond_t cond_consumer;
pthread_cond_t cond_procedure;
pthread_mutex_t mutex;// 生产,在 buffer 中放入一个值
void put(int value) {assert(count == 0);count = 1;buffer = value;
}
// 消费,取出 buffer 中的值
int get() {assert(count == 1);count = 0;return buffer;
}/***********第四版本**********/
// 假设生产者唤醒了第一个消费者,消费者又唤醒了第二个生产者,第二个生产者
// 之后又睡眠.这样三个线程都在睡眠.
// 核心问题就是,消费者只能唤醒生产者,生产者只能唤醒消费者.
// 生产者
void *producer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);while (count == 1) {pthread_cond_wait(&cond_procedure, &mutex);}put(i);pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond_consumer);}return NULL;
}
// 消费者
void *consumer(void *arg) {int loops = *((int *)arg);for (int i = 0; i < loops; i++) {pthread_mutex_lock(&mutex);while (count == 0) {pthread_cond_wait(&cond_consumer, &mutex);}int temp = get();pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond_procedure);printf("消费:%d\n", temp);}return NULL;
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond_consumer, NULL);pthread_cond_init(&cond_procedure, NULL);pthread_t p1, p2, p3;int arg = 100;int arg1 = 50;int arg2 = 50;pthread_create(&p1, NULL, producer, &arg);pthread_create(&p2, NULL, consumer, &arg1);pthread_create(&p3, NULL, consumer, &arg2);// 等待线程结束pthread_join(p1, NULL);pthread_join(p2, NULL);pthread_join(p3, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond_consumer);pthread_cond_destroy(&cond_procedure);return 0;
}

运行结果:

*** chap30_条件变量 % gcc -o a con_prodece3.c
*** chap30_条件变量 % ./a
消费:0
消费:2
消费:1
...
消费:97
消费:98
消费:99

可以看到运行得很好,成功地解决了并发、虚假唤醒以及全部线程都睡眠的情况。

二、总结

我们看到了引入锁之外的另一个重要同步原语:条件变量。当某些程序状态不符合要求时,通过允许线程进入休眠状态,条件变量使我们能够漂亮地解决许多重要的同步问题,包括著名的(仍然重要的)生产者/消费者问题,以及覆盖条件。

相关文章:

《OSTEP》条件变量(chap30)

〇、前言 本文是对《OSTEP》第三十章的实践与总结。 一、条件变量 #include <pthread.h> #include <stdio.h> #include <assert.h>int buffer; int count 0; // 资源为空// 生产,在 buffer 中放入一个值 void put(int value) {assert(count 0);count 1…...

MySQL的索引和复合索引

由于MySQL自动将主键加入到二级索引&#xff08;自行建立的index&#xff09;里&#xff0c;所以当select的是主键或二级索引就会很快&#xff0c;select *就会慢。因为有些列是没在索引里的 假设CA有1kw人咋整&#xff0c;那我这个索引只起了前一半作用。 所以用复合索引&am…...

关于mac下pycharm旧版本没删除的情况下新版本2023安装之后闪退

先说结论&#xff0c;我用的app cleaner 重新删除的pycharm &#xff0c;再重新安装即可。在此记录一下 之前安装的旧版的2020的pycharm&#xff0c;因为装不了新的插件&#xff0c;没办法就升级了。新装2023打开之后闪退&#xff0c;重启系统也不行&#xff0c;怀疑是一起破解…...

Django中如何让DRF的接口针对前后台返回不同的字段

在Django中&#xff0c;使用Django Rest Framework&#xff08;DRF&#xff09;时&#xff0c;可以通过序列化器&#xff08;Serializer&#xff09;和视图&#xff08;View&#xff09;的组合来实现前后台返回不同的字段。这通常是因为前后台对数据的需求不同&#xff0c;或者…...

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法&#xff0c;简单地说就是把相似的数据样本分到一组&#xff08;簇&#xff09;&#xff0c;聚类的过程&#xff0c;我们并不清楚某一类是什么&#xff08;通常无标签信息&#xff09;&#xff0…...

getid3 获取视频时长

1、首先&#xff0c;我们需要先下载一份PHP类—getid3https://codeload.github.com/JamesHeinrich/getID3/zip/master 2.我在laravel6.0 中使用 需要在composer.json 自动加载 否则系统访问不到 在命令行 执行 composer dump-autoload $getID3 new \getID3();//视频文件需要放…...

如何知道一个程序为哪些信号注册了哪些信号处理函数?

https://unix.stackexchange.com/questions/379694/is-there-a-way-to-know-if-signals-are-present-in-your-application-and-which-sign 使用 strace...

34 mysql limit 的实现

前言 这里来看一下 我们常见的 mysql 分页的 limit 的相的处理 这个问题的主要是来自于 之前有一个需要处理 大数据量的数据表的信息, 将数据转移到 es 中 然后就是用了最简单的 “select * from tz_test limit $pageOffset, $pageSize ” 来分页处理 但是由于 数据表的数…...

jbase实现申明式事务

对有反射的语言&#xff0c;申明式事务肯定不可少。没必要没个人都try&#xff0c;catch写事务&#xff0c;写的不好的话还经常容易锁表&#xff0c;为此给框架引入申明式事务。申明式既字面意思&#xff0c;在需要事务的方法前面加一个申明&#xff0c;那么框架保证事务。 首…...

如何在在线Excel文档中规范单元格输入

在日常的工作中&#xff0c;我们常常需要处理大量的数据。为了确保数据的准确性和可靠性。我们需要对输入的数据进行规范化和验证。其中一个重要的方面是规范单元格输入。而数据验证作为Excel中一种非常实用的功能&#xff0c;它可以帮助用户规范单元格的输入&#xff0c;从而提…...

力扣138:随机链表的复制

力扣138&#xff1a;随机链表的复制 题目描述&#xff1a; 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff…...

C语言左移与右移学习

在学习左移与右移之前&#xff0c;我们首先要学习两种移位运算&#xff1a;逻辑移位和算数移位。 逻辑位移&#xff1a;移出去的位丢弃&#xff0c;空缺位用0补充。 算数位移&#xff1a;移出去的位丢弃&#xff0c;空缺位用符号位补充。 左移 左移是高位溢出&#xff0c;低…...

asp.net core mvc之 视图

一、在控制器中找到匹配视图&#xff0c;然后渲染成 HTML 代码返回给用户 public class HomeController : Controller {public IActionResult Index(){return View(); //默认找 Views/Home/Index.cshtml &#xff0c;呈现给用户} &#xff5d; 二、指定视图 1、控制器 publ…...

ChatGLM3 tool_registry.py 代码解析

ChatGLM3 tool_registry.py 代码解析 0. 背景1. tool_registry.py 0. 背景 学习 ChatGLM3 的项目内容&#xff0c;过程中使用 AI 代码工具&#xff0c;对代码进行解释&#xff0c;帮助自己快速理解代码。这篇文章记录 ChatGLM3 tool_registry.py 的代码解析内容。 1. tool_re…...

js实现定时刷新,并设置定时器上限

定时器 在js中&#xff0c;有两种定时器&#xff1a; 倒计时定时器 倒计时定时器&#xff0c;也叫延时定时器或一次性定时器 功能&#xff1a;倒计时多长时间后执行某个动作 语法&#xff1a;setTimeout(function, timeout); 返回值&#xff1a;int类型&#xff0c;当前定时器…...

常用Linux命令

df -h #查看磁盘 kill -9 pid #强制关闭程序 ifconfig #查看网卡信息 last …...

【C++】获取指定点所在屏幕的尺寸

问题 多个显示器时&#xff0c;获取指定点所在的显示器的尺寸。 分析 之前整理过获取屏幕尺寸的方法&#xff1a;https://blog.csdn.net/m0_43605481/article/details/125024500多显示器时&#xff0c;需要用到GetSystemMetrics、EnumDisplayDevices、EnumDisplaySettings函…...

软文发布如何选择对应的媒体

企业做软文推广第一步&#xff0c;就是选择合适的媒体进行投放&#xff0c;然而许多企业不知道如何选择合适的媒体导致推广工作十分被动&#xff0c;无法取得效果&#xff0c;今天媒介盒子就来和大家分享&#xff0c;企业应该如何选择对应的媒体。 一、 媒体类型 根据软文类型…...

Django如何创建表关系,Django的请求声明周期流程图

【1】表与表之间的关系 一对一 左表的一条记录对应右表的一条记录&#xff0c;反之亦然 多对一 左表的一条记录对应右表的多条记录&#xff0c;反之不成立 多对多 左表的一条记录对应右表的多表记录&#xff0c;反之成立 【2】django中创建表关系 class Book(models.Model):t…...

微服务-我对Spring Clound的理解

官网&#xff1a;https://spring.io/projects/spring-cloud 官方说法&#xff1a;Spring Cloud 为开发人员提供了快速构建分布式系统中一些常见模式的工具&#xff08;例如配置管理、服务发现、熔断器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话…...

从面试官视角看嵌入式C/C++:那些年我们踩过的坑与避开的雷

嵌入式C/C面试官的深度思考&#xff1a;技术考察背后的逻辑与实战智慧 在嵌入式开发领域&#xff0c;技术面试往往是一场无声的博弈。作为面试官&#xff0c;我们设计的每一个问题都像精心布置的棋盘&#xff0c;等待着候选人展示他们的思维路径。但这场博弈的目的不是难倒对方…...

从SD卡到EMMC:手把手教你用U-Boot的tftp和update_mmc命令完成系统引导迁移

从SD卡到EMMC&#xff1a;U-Boot引导迁移全流程实战指南 当开发板通过SD卡成功启动U-Boot后&#xff0c;如何将引导程序永久写入板载EMMC&#xff1f;这不仅关乎设备能否独立启动&#xff0c;更直接影响产品化部署的可靠性。本文将手把手带你完成从临时启动到永久固件部署的关键…...

LCD1602显示异常?51单片机驱动DS1302时钟的5个常见坑点及解决方法

51单片机驱动DS1302与LCD1602的五大实战陷阱与破解之道 1. 通信协议配置不当导致的显示异常 当LCD1602显示乱码或完全不亮时&#xff0c;首先需要检查通信协议配置。51单片机与LCD1602的通信需要严格遵循时序要求&#xff0c;常见问题包括&#xff1a; 初始化序列缺失&#xff…...

深入PyTorch源码:grid_sample的坐标映射到底是怎么算的?(从-1,1到像素索引)

深入PyTorch源码&#xff1a;grid_sample的坐标映射到底是怎么算的&#xff1f; 当你第一次使用grid_sample时&#xff0c;可能会被它神奇的坐标变换能力所吸引——它能够将归一化的[-1,1]坐标精确映射到输入特征图的像素索引上。但当你需要调试输出异常或优化性能时&#xff…...

从时钟树到中断回调:图解S32K3的STMPIT完整工作流程

从时钟树到中断回调&#xff1a;图解S32K3的STM&PIT完整工作流程 在汽车电子领域&#xff0c;精确的定时控制如同车辆的神经系统&#xff0c;协调着各个ECU的运作节奏。S32K3系列MCU作为NXP面向新一代汽车架构的核心控制器&#xff0c;其内置的STM&#xff08;系统定时器模…...

【嵌入式Linux】---- 从设备树到应用层:基于PetaLinux与SDK的GPIO驱动全链路开发与调试

1. 嵌入式Linux开发环境搭建 第一次接触嵌入式Linux开发的朋友可能会被各种工具链和环境配置搞得晕头转向。我刚开始做Zynq平台开发时&#xff0c;光是搭建环境就折腾了好几天。现在回想起来&#xff0c;其实只要抓住几个关键步骤&#xff0c;整个过程就会顺利很多。 首先得准备…...

linuxdeployqt企业级应用部署:大规模分发与维护的最佳实践

linuxdeployqt企业级应用部署&#xff1a;大规模分发与维护的最佳实践 【免费下载链接】linuxdeployqt Makes Linux applications self-contained by copying in the libraries and plugins that the application uses, and optionally generates an AppImage. Can be used for…...

彻底解决Windows音量栏干扰的专业方案:HideVolumeOSD技术深度解析

彻底解决Windows音量栏干扰的专业方案&#xff1a;HideVolumeOSD技术深度解析 【免费下载链接】HideVolumeOSD Hide the Windows 10 volume bar 项目地址: https://gitcode.com/gh_mirrors/hi/HideVolumeOSD 在Windows 10/11系统中&#xff0c;音量控制条&#xff08;OS…...

别再只用微信授权了!手把手教你用小程序云开发实现账号密码登录注册(附完整源码)

突破微信授权限制&#xff1a;小程序云开发构建完整账号体系实战指南 每次看到小程序弹出"微信授权登录"的界面时&#xff0c;你有没有想过——如果用户拒绝授权&#xff0c;你的应用就彻底失去了这个用户&#xff1f;去年我们团队就遇到过这样的尴尬&#xff1a;一个…...

BepInEx完全指南:3步让任何Unity游戏变身插件平台

BepInEx完全指南&#xff1a;3步让任何Unity游戏变身插件平台 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx BepInEx是一个强大的游戏插件框架&#xff0c;专门为Unity Mono、IL2…...