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.mzph.cn/pingmian/37915.shtml

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

相关文章

C语言力扣刷题11——打家劫舍1——[线性动态规划]

力扣刷题11——打家劫舍1和2——[线性动态规划] 一、博客声明二、题目描述三、解题思路1、线性动态规划 a、什么是动态规划 2、思路说明 四、解题代码&#xff08;附注释&#xff09; 一、博客声明 找工作逃不过刷题&#xff0c;为了更好的督促自己学习以及理解力扣大佬们的解…

Java中System的用法

System指的是当前进程运行的操作系统&#xff0c;属于java.lang包下面的类 常见的用法有以下几种&#xff1a; 第一种简单,我们直接上第二种方法吧 currentTimeMills()用法 // 演示currentTimeMillis方法public static void main(String[] args) {// 获取当前时间所对应的毫秒…

Apache Ranger 2.4.0 安装部署

1、安装ranger admin 2、源码编译Ranger wget https://www.apache.org/dist/ranger/2.4.0/apache-ranger-2.4.0.tar.gz tar zxvf apache-ranger-2.4.0.tar.gz cd apache-ranger-2.4.0 mvn -Pall clean mvn clean package -DskipTests maven settting可以设置阿里云进行资源下载…

昇思25天学习打卡营第4天|扩散模型

文章目录 昇思MindSpore应用实践基于MindSpore的Diffusion扩散模型1、Diffusion Models 简介2、构建 Diffusion Model 的准备工作3、Attention 机制4、条件 U-Net5、Diffusion 正向过程6、Diffusion 反向过程7、Diffusion 模型训练 Reference 昇思MindSpore应用实践 本系列文章…

【Qt知识】Geometry属性

一、走进Geometry的世界 Geometry属性是Qt框架中用于处理和操作几何形状的一系列类的集合。它包括了QPoint、QPointF、QSize、QSizeF、QRect和QRectF等。这些类分别代表点、大小、矩形等基本几何概念&#xff0c;它们的存在让图形界面的创建变得既简单又直观。 位置和尺寸。 其…

css 滚动词云

css javascript 实现滚动词云效果 // 163css.js var radius 120; var dtr Math.PI / 180; var d 300; var mcList []; var active false; var lasta 1; var lastb 1; var distr true; var tspeed 10; var size 250; var mouseX 0; var mouseY 0; var howElliptic…

MySQL高级-MVCC-隐藏字段

文章目录 1、介绍2、测试2.1、进入服务器中的 /var/lib/mysql/atguigu/2.2、查看有主键的表 stu2.3、查看没有主键的表 employee2.3.1、创建表 employee2.3.2、查看表结构及其其中的字段信息 1、介绍 ---------------- | id | age | name | ---------------- | 1 | 1 | Js…

python读取语文成绩 青少年编程电子学会python编程等级考试三级真题解析2022年3月

目录 python读取语文成绩 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python读取语文成绩 2022年3月 python编程等级考试级编程题 一、题目…

【Qt】之【Bug】大量出现“未定义的标识符”问题

背景 构建时出现大量错误 原因 中文注释问题 解决 方法1. 报错代码附近的中文注释全部删掉。。。 方法2. 报错的文件添加 // Chinese word comment solution #pragma execution_character_set("utf-8")

第二天:ALOAM前端讲解【第3部分】

(2)面特征 点到面的距离公式: d H = ∣ ( X ~ ( k + 1 , i ) L − X ˉ ( k , j ) L ) ⋅ ( ( X ˉ ( k , j ) L − X ˉ ( k , l ) L ) ( X ˉ ( k , j ) L − X ˉ ( k , m ) L ) ) ∣ ∣ ( X ˉ ( k , j ) L − X ˉ ( k , l ) L ) ( X ˉ ( k , j ) L − X ˉ ( k ,…

Linux常用命令大全(超详细!!!)

文章目录 1.Linux是什么1.1 关于Linux我们主要学习什么1.1 学习Linux常见命令的前置知识 2. Linux常见命令2.1 ls命令2.2 cd命令2.3 pwd命令2.4 touch命令2.5 cat命令2.6 echo命令2.7 vim命令2.8 mkdir 命令2.9 rm命令2.10 cp命令2.11 mv命令2.12 grep命令2.13 ps命令2.14 nets…

文华财经通达信同花顺期货通盘立方博易大师主图指标公式源码

买线:EMA(C,2); 卖线:EMA(SLOPE(C,21)*20C,42); BU:CROSS(买线,卖线); SEL:CROSS(卖线,买线); STICKLINE1(买线>卖线,LOW,MIN(O,C),0.1,1),COLORRED; STICKLINE1(买线>卖线,MAX(O,C),HIGH,0.1,1),COLORRED; STICKLINE(买线>卖线,CLOSE,OPEN,8,1),COLORRED; STI…

【简单讲解下OneFlow深度学习框架】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

AD如何将厂家的元器件库,添加到自己的元器件库

首先&#xff0c;去官网下载对应芯片的原理图和封装&#xff0c;之后用分别双击打开原理图和封装。之后打开自己的原理图库和封装库。如下图&#xff1a; 打开原理图和封装后框选&#xff0c;之后crlC复制&#xff0c;之后点开自己的原理图库和封装库&#xff0c;随便单击一个元…

Linux(Ubuntu20.04)系统中安装deb软件包错误(依赖关系问题-仍未被配置)解决的办法

在Ubuntu16.04下采用如下dpkg命令安装deb软件安装包时&#xff1a; sudo dpkg -i XXXX.deb 发生安装失败&#xff0c;返回信息为&#xff02;正处理时有错误发生&#xff02;&#xff0c;并且在安装过程中出现&#xff02;依赖关系问题-仍未被配置&#xff02;的提示&#xff0…

51单片机第11步_在C语言中插入汇编语言

本章重点介绍如何在C语言中插入汇编语言。要不是有记录&#xff0c;真不知道怎么搞。 /* 你在 Project Workspace窗口中,将光标移到DELAY.c处,点下鼠标右键,选择"Options for file DELAY.c", 点击右边的"Generate Assembler SRC File"和“Assemble SRC …

【PL理论深化】(12) Ocaml 语言:高阶函数 | map 函数 | filter 函数 | fold 函数

&#x1f4ac; 写在前面&#xff1a;在函数式编程中&#xff0c;除了递归函数外&#xff0c;还经常使用高阶函数。高阶函数是指接收其他函数作为参数或返回另一个函数的函数。高阶函数通过抽象编程模式以实现重用&#xff0c;使程序可以在更高层次上进行编写。让我们重点看看常…

K8S基础简介

用于自动部署&#xff0c;扩展和管理容器化应用程序的开源系统。 功能&#xff1a; 服务发现和负载均衡&#xff1b; 存储编排&#xff1b; 自动部署和回滚&#xff1b; 自动二进制打包&#xff1b; 自我修复&#xff1b; 密钥与配置管理&#xff1b; 1. K8S组件 主从方式架…

socket编程常见操作

1、连接的建立 分为两种&#xff1a;服务端处理接收客户端的连接&#xff1b;服务端作为客户端连接第三方服务 //作为服务端 int listenfd socket(AF_INET, SOCK_STREAM, 0); bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) listen(listenfd, 10); //…

SARscape打开Sentinel1A SAR SLC产品(CSDB_20240630)

1.打开envi&#xff0c;在右侧工具包栏输入“sentinel-1”&#xff0c;并点击打开工具包。 2. 弹出文件导入界面&#xff0c;点击右侧Browse按钮。 3. 选在本地下载好的Sentinel1产品&#xff0c;文件路径最好全是英文&#xff0c;不要出现中文和特殊字符。 4 点击下方“Exec”…