Linux线程同步【拿命推荐版】

目录

🚩引言

🚩听故事,引概念

🚩生产者消费者模型

 🚀再次理解生产消费模型

 🚀挖掘特点

 🚩条件变量

🚀条件变量常用接口

 🚀条件变量的原理


🚩引言

上一篇博客,我们集中讨论了Linux线程互斥相关的概念,但随着互斥锁的使用,也容易产生线程饥饿问题,所以我们有必要学习一下Linux线程同步相关的概念。接下来我们开始学习。

🚩听故事,引概念

假设在学校有一个VIP学霸自习室,这个自习室非常的豪华,但是只有一张桌子,一次只能允许一个人去上自习,这个自习室的钥匙就在门口放着,谁离钥匙近,谁就获得钥匙,就可以在利用这个自习室自习。有一天,同学张三为了在自习室中学习,享受那种超棒的学习环境,他早上5点就爬了起来。所以,等到他来到自习室门口时,没有人。他非常高兴的取得了钥匙,开开门,然后把门一反锁,开始自习。一转眼上午了,张三感觉肚子饿了,但他不想放弃他早起得到的机会,于是,他就把门锁上了,然后随身带着钥匙,门口的同学看着他的行为都非常的愤怒,但是又无奈,谁让人家来的早呢!不一会儿,张三吃完饭回来了,开开门,继续上自习。这家伙有闷头学了3个小时,突然肚子痛,要上厕所,他又带着钥匙上厕所。上完之后继续学习。可能学的时间太长了,不想学了,有好几次都开门准备放回钥匙,但每次都想到:下次不知道什么时候才能再次在这里上自习了。他放回钥匙之际,此时,他是离要是最近的人,对钥匙的竞争能力最强,所以他又获得了钥匙即自习室的使用权。张三的行为错了吗?他没错,但是不合理

门外的同学看见他这么干都非常生气,决定把这个情况反映给值班的老师,老师认为这样就是不太好,决定结束自习的同学要想再次获得自习室的使用权,必须要排队。

张三照常把钥匙放了回去,正当他想伸手再次取得钥匙时,旁边的老师说:"去排队去"。张三无奈的去排队去了。心想:“卧槽,失算了,之前不是这样的规则呀”。


听完这个故事,有些问题我们来思考这样几个问题:

  1. 按照之前的规则,张三同学反复申请这间自习室的使用权,是建立这间自习室的初衷吗?不是。那对其他同学公平吗?不公平,有的同学可能在门口等了一天,都没有使用到这间自习室。
  2. 这间自习室的钥匙就像是一把互斥锁,自习室就像是临界资源,申请这间自习室的使用权的同学就像是不同的执行流。所以,也会发生有的执行流因为竞争能力太强,就像这个同学张三,总是可以获得这把锁,然后进行访问。但是这样就会造成其他的执行流因为竞争不到对应的锁,而处于线程饥饿状态。这种状态是不利于充分利用资源的。在我们之前的抢票的代码中,我们也发现总是那一个执行流在抢票,所以这个问题要解决。而解决方案就是Linux线程同步就是让执行流按照一定的顺序(不一定是绝对的顺序)来获得访问相关临界资源的权利。

🚩生产者消费者模型

 在现实生活中,我们学生就是典型的消费者,而生产者就是工厂。

假设,有一天我想吃火腿肠了,我就跑到一个加工火腿肠的工厂,对那里的工作人员说:“给我加工火腿肠,多少钱,我给你”。工作人员听了我说的话,一定以为我是个傻子。这是因为制作火腿肠需要机器,而打开机器需要成本的,一根火腿肠指定是远远不够的,今天我来了,买了一根火腿肠,给我现场加工了,明天别人来了,又只要一根。我相信这个工厂早晚得倒闭。

所以,在生产者和消费者之间,一定还存在超市这样一个交易场所。超市的作用是集中需求,分发产品,是对生产者生产的商品的临时保存。超市存在的另一个原因是:工厂一般远离消费者,超市的存在可以更加方便消费者消费。

消费者消费吃火腿肠的同时,生产者可能在放假;生产者在生产火腿肠的同时,消费者可能又没有在吃火腿肠。这种行为用计算机术语来形容就是:生产者和消费者实现了解耦。解耦就是互相不干扰的意思。超市在计算机体系中就是共享资源--->消费者需要通过访问超市来获取商品,生产者需要通过访问超市来销售商品。在超市中类似超市这样的作用的区域我们称之为缓冲区。

我们刚刚说:生产者消费者模型实现了生产和消费的解耦。那在我们写代码的过程中,有没有强耦合的代码呢?有的。

最显著的例子就是函数调用。我们认为的函数的过程一般是这个样子的:首先实参通过形参传递给函数,然后经过函数体内部复杂的运算,输出运输的结果。

  • 调用方:生产了数据。
  • 形参变量:暂时保存数据。‘
  • 目标函数:消费了数据。

假如,我们代码中main函数中调用的func函数。我们把数据传给func函数的时候,main函数在做什么?它什么都做不了,只能等待调用func函数结束。这就是强耦合关系的例子。 


 🚀再次理解生产消费模型

我们依旧使用上面的超市的模型来深度挖掘生产消费模型的特点。生产者对应一个或者多个线程。消费者对应一个或者多个线程。

①生产者和生产者之间是什么关系?

这个大家都知道,一定是互斥即竞争关系。俗话说同行是冤家,在超市展柜上展出自己的商品时,只能同一个品牌上完货,然后另一个火腿肠品牌再上货。负责生产数据的线程之间的关系也是如此。

②消费和消费者之间是什么关系呢?

试想一下:假如过几天就是世界末日,你和同学两个人去超市买火腿肠,但是火腿肠只有一根了,你们两个肯定因为这根火腿肠而吵起来。所以消费者和消费者之间同样是互斥关系

③生产者和消费者之间是什么关系呢?

假如有一次,你去超市买火腿肠,同时超市的工作人员或者火腿肠的厂家正在上货。你们两个此时比较尴尬,是先上货呢?还是先让我拿呢?是可以沟通的。但是对于计算机而言却不是这样的,操作系统内的线程确实无法沟通的。假如在操作系统内有一块空间,一个线程正在读取这块空间中的内容,与此同时,一个线程正在修改这块空间里的内容,毫无疑问读取的内容一定发生了改变。这是不合理的,所以生产者和消费者之间首先要保持着互斥的关系,不让其同时访问

假如有这么一个节日,节日期间流行吃火腿肠,所以超市里的供不应求。张三同学为了吃上火腿肠,每天准时准点来到超市问工作人员火腿肠到了没有,假如每天会有很多人前来询问,这对这位工作人员来说,也是一种负担。所以这位工作人员就要求加张三的维信,有火腿肠了就通知张三,这样张三就不用每天来到这里询问了。假如有这样一段时间,是火腿肠的淡季,超市里堆积了大量的火腿肠,但是工厂还源源不断的生产着,所以厂家每天来超市询问是否要进货。这也让超市的工作人员非常讨厌。一天,这位工作人员也加上了厂家的微信,如果需要进货,就给厂家发消息。所以这样,就间接维护了生产者和消费者的同步关系

总结起来,生产消费模型要遵守321的原则。只要我们想写生产者消费者模型,我们的本质是要维护321原则。

3种关系

生产者和生产者要保持互斥关系②消费者和消费者之间要保持互斥关系③消费者和生产者之间既要保持互斥关系也要保持同步关系。

2种角色

生产者线程,消费者线程

1个交易场所

一段特定结构的缓冲区 

 🚀挖掘特点

①生产线程和消费线程进行解耦

②支持生产者和消费者一段时间的忙闲不均的问题

有没有可能在一段时间内,生产者的生产能力很强,但是消费者的消费水平很低;或者生产者的生产水平很弱,但是消费者的消费水平很强。但是由于中间的超市的存在,可以平衡生产者和消费者之间生产和消费的问题。在我们的计算机内部,也是如此的

③提高效率

在社会发展中,出现了超市这样的事物,一定有它存在的道理和意义。假如没有超市的存在,我们需要购买商品时,就需要跑到工厂,相比于在超市中购买,肯定要浪费更多的时间。而工厂的工作人员就需要抽出人力来销售自己生产的商品,也浪费人力资源。所以生产者消费者模型的出现,可以提高我们的效率。在我们的计算机内部也是如此,同样适用。关于提高效率这块的内容,我们后边还会说明。


所以,我们现在是不是有能力将函数调用解耦呢?

是的,现在我们就可以实现函数调用解耦了。具体我们可以这样做:

我们先定义一个缓冲区,负责存储实参。我们先将要喂给调用函数作为实参的数据存储在缓冲区内,然后调用函数可以随时从缓冲区内读取数据,作为实参进行处理,然后输出结果。这样两个执行流就由串行执行变为并发执行,真正意义上实现了解耦。


但我们忽略了一个问题:生产者和消费者的关系是互斥之间的关系,就是同一时间,仅允生产线程和消费线程中的一个线程访问缓冲区(也就是临界区)。假如生产者的优先级非常高,同时缓冲区的数据已满,不允许再写入数据,但是生产线程却不断的进行查询,这样也就会导致一个线程一直访问临界资源,就会造成我们刚刚说的自习室问题。具体如图:

如上图,结合这份伪代码,大家应该可以理解。这时,我们就要提出条件变量的概念了。

 🚩条件变量

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。这句话应该如何理解呢?

如上图,若干个线程互斥性的访问ticket这个变量,但只有剩余票数大于0时,才能完成抢票的动作,否则什么也干不了。此时如果一个线程的优先级很高,那么它就会不停的查询ticket的值,造成一定程度上的无用查询。这时我们就可以设定一个条件变量,等到ticket大于0时,通知该线程来抢票,不用一直在这里查询了。这就是条件变量的用处。

🚀条件变量常用接口

// 所有条件变量的相关函数都在该头文件下
#include <pthread.h>
// 创建一个条件变量
pthread_cond_t +变量名
// 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
// 对一个条件变量进行初始化,参数:cond:要初始化的条件,attr:NULL
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
// 如果这个条件变量是静态的或者全局的,也可以这样初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 等待条件满足,参数:cond:要在这个条件变量上等待,mutex:互斥量,后面详细解释
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
// 如下两个函数是唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

注意:这些函数如果有返回值,默认成功的话,返回0;失败的话,错误码被设置。

接下来,见见猪跑

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1(void *arg)
{while (1){pthread_cond_wait(&cond, &mutex);printf("活动\n");}
}
void *r2(void *arg)
{while (1){pthread_cond_signal(&cond);sleep(1);}
}
int main(void)
{pthread_t t1, t2;pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, r1, NULL);pthread_create(&t2, NULL, r2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);
}

 🚀条件变量的原理

接下来,我们讲一个故事

一到招聘季,互联网公司总是派面试官以出差的形式去全国各地招收优秀的人才。他们每到一处地方,一般都会包下一个宾馆的一层楼,作为他们的面试场所。假设他们来到了济南,然后把面试场所安排在了万达酒店。

在一间屋子里,面试官正面试着一位同学,不一会儿面完了。此时门口挤满了求职者,他们都高举着自己的简历,然后高喊:"我先来的,应该先面我"。无奈,面试官只能挑喊的声音大的先面试。等到下午,吸取了经验,面试官在门口划定了一块区域,说:“只从这里选人面试,并且排队”

 

 其中,这块区域就像条件变量。

当条件不满足时,我们线程必须去某些定义好的条件变量下等待。

说到这里,本篇内容就结束了,我们下期博客再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/364978.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解锁横向招聘:创新您的人才搜索

技能差距仍然是面试官面临的问题之一。在这些空缺职位中&#xff0c;很难找到合适的技能候选人&#xff0c;特别是高级职位或以上职位。另一方面&#xff0c;申请人也发现很难找到一份适合自己的工作&#xff0c;因为他们抱怨工作要求太窄或太具体。在具有挑战性的职位招聘环境…

魔行观察-烤匠麻辣烤鱼-开关店监测-时间段:2011年1月 至 2024年6月

今日监测对象&#xff1a;烤匠麻辣烤鱼&#xff0c;监测时间段&#xff1a;2011年1月 至 2024年6月 本文用到数据源获取地址 魔行观察http://www.wmomo.com/ 品牌介绍&#xff1a; 2013年&#xff0c;第一家烤匠在成都蓝色加勒比广场开业&#xff0c;随后几年成都国金中心店…

Qt开发 | Qt界面布局 | 水平布局 | 竖直布局 | 栅格布局 | 分裂器布局 | setLayout使用 | 添加右键菜单 | 布局切换与布局删除重构

文章目录 一、Qt界面布局二、Qt水平布局--QHBoxLayout三、Qt竖直布局四、Qt栅格布局五、分裂器布局代码实现六、setLayout使用说明七、布局切换与布局删除重构1.如何添加右键菜单2.布局切换与布局删除重构 一、Qt界面布局 Qt的界面布局类型可分为如下几种 水平布局&#xff08;…

木各力“GERRI”被“GREE”格力无效宣告成功

近日“GERRI”被“GREE”格力无效宣告成功&#xff0c;“GERRI”和“GREE”近似不&#xff0c;如果很近似当初就不会通过初审和下商标注册证&#xff0c;但是如果涉及知名商标和驰名商标&#xff0c;人家就可以异议和无效。 “GERRI”在被无效宣告时&#xff0c;引用了6个相关的…

【python刷题】【深基5.例5】旗鼓相当的对手

题目描述 算法思路 用二维数组data存放成绩数据。双重循环遍历所有的组合&#xff0c;因为自己不能和自己比&#xff0c;所以要注意内层遍历的起始位置。新建一个数组用来得出各个科目的分差&#xff0c;便于代码的书写。由于分差计算出来会出现负数&#xff0c;所以比较的时候…

探索MySQL核心技术:理解索引和主键的关系

在数据密集型应用中&#xff0c;数据库的性能往往是决定一个应用成败的重要因素之一。其中&#xff0c;MySQL作为一种开源关系型数据库管理系统&#xff0c;以其卓越的性能和丰富的功能被广泛应用。而在MySQL数据库优化的众多技巧中&#xff0c;索引和主键扮演着极其重要的角色…

Windows怎么实现虚拟IP

在做高可用架构时&#xff0c;往往需要用到虚拟IP&#xff0c;在linux上面有keepalived来实现虚拟ip的设置。在windows上面该怎么弄&#xff0c;keepalived好像也没有windows版本&#xff0c;我推荐一款浮动IP软件PanguVip&#xff0c;它可以实现windows上面虚拟ip的漂移。设置…

WordPress中文网址导航栏主题风格模版HaoWa

模板介绍 WordPress响应式网站中文网址导航栏主题风格模版HaoWa1.3.1源码 HaoWA主题风格除行为主体导航栏目录外&#xff0c;对主题风格需要的小控制模块都开展了敞开式的HTML在线编辑器方式的作用配备&#xff0c;另外预埋出默认设置的编码构造&#xff0c;便捷大伙儿在目前…

常用字符串方法<python>

导言 在python中内置了许多的字符串方法&#xff0c;使用字符串方法可以方便快捷解决很多问题&#xff0c;所以本文将要介绍一些常用的字符串方法。 目录 导言 string.center(width[,fillchar]) string.capitalize() string.count(sub[,start[,end]]) string.join(iterabl…

Qt学习之ui创建串口助手

一、串口简介 二、Qt编写串口助手 1、创建Qt工程 选择MinGW 64-bit 点击下一步完成&#xff0c;工程创建完成。 使用串口模块&#xff0c;需要在工程文件.pro中添加以下代码&#xff0c;不添加的话&#xff0c;会报错。 或者在core gui 后输入 serialport 也可以 2、配置UI…

【Unity】RPG2D龙城纷争(六)关卡编辑器之角色编辑

更新日期&#xff1a;2024年6月26日。 项目源码&#xff1a;第五章发布&#xff08;正式开始游戏逻辑的章节&#xff09; 索引 简介一、角色编辑模式1.将字段限制为只读2.创建角色&#xff08;刷角色&#xff09;3.预览所有角色4.编辑选中角色属性5.移动角色位置6.移除角色 简介…

vue中【事件修饰符号】详解

在Vue中&#xff0c;事件修饰符是一种特殊的后缀&#xff0c;用于修改事件触发时的默认行为。以下是Vue中常见的事件修饰符的详细解释&#xff1a; .stop 调用event.stopPropagation()&#xff0c;阻止事件冒泡。当你在嵌套元素中都有相同的事件监听器&#xff08;如click事件…

【Linux系统】进程替换 自主实现shell(简易版)

1.先看代码 && 现象 我们用exec*函数执行新的程序&#xff0c; exec*系列的函数&#xff0c;执行完毕后&#xff0c;后续的代码不见了&#xff0c;因为被替换了。 execl的返回值可以不关心了&#xff0c;只要替换成功&#xff0c;就不会向后继续运行&#xff0c;只要…

单片机是否有损坏,怎沫判断

目录 1、操作步骤&#xff1a; 2、单片机损坏常见原因&#xff1a; 3、 单片机不工作的原因&#xff1a; 参考&#xff1a;细讲寄存器读写与Bit位操作原理--单片机C语言编程Bit位的与或非屏蔽运算--洋桃电子大百科P019_哔哩哔哩_bilibili 1、操作步骤&#xff1a; 首先需要…

前置章节-熟悉Python、Numpy、SciPy和matplotlib

目录 一、编程环境-使用jupyter notebook 1.下载homebrew包管理工具 2.安装Python环境 3.安装jupyter 4.下载Anaconda使用conda 5.使用conda设置虚拟环境 二、学习Python基础 1.快排的Python实现 (1)列表推导-一种创建列表的简洁方式 (2)列表相加 2.基本数据类型及运…

Navicat上新啦

前言 Navicat&#xff0c;在数据库界&#xff0c;几乎是一个神奇的存在&#xff0c;似乎统治了数据库开发工具的“一片天”。且看下图&#xff1a; 红的蓝的绿的橙的…&#xff0c;可以说&#xff0c;留给它的color不多了。 那么商业BI到服务监控、从云托管到云协作&#xff…

最强文生图模型Stable Diffusion 3 Medium 正式开源

Stability AI 宣布 Stable Diffusion 3 Medium 现已开源&#xff0c;是 Stable Diffusion 3 系列中最新、最先进的文本生成图像 AI 模型 —— 官方声称是 “迄今为止最先进的开源模型”&#xff0c;其性能甚至超过了 Midjourney 6。 Stable Diffusion 3 Medium 模型规格参数达到…

【鸿蒙学习笔记】位置设置

官方文档&#xff1a;位置设置 目录标题 align&#xff1a;子元素的对齐方式direction&#xff1a;官方文档没懂&#xff0c;看图理解吧 align&#xff1a;子元素的对齐方式 Stack() {Text(TopStart)}.width(90%).height(50).backgroundColor(0xFFE4C4).align(Alignment.TopS…

Spring+Vue集成AOP系统日志

新建logs表 添加aop依赖 <!-- aop依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency> 新建获取ip地址工具类 import javax.servlet.http.H…

昇思25天学习打卡营第12天|ShuffleNet图像分类

1. 学习内容复盘 ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一样主要应用在移动端&#xff0c;所以模型的设计目标就是利用有限的计算资源来达到最好的模型精度。ShuffleNetV1的设计核心是引入了两种操作&a…