学习系统编程No.34【线程同步之信号量】

引言:

北京时间:2023/7/29/16:34,一切尽在不言中,前几天追了几部电视剧,看了几部电影,刷了n个视屏,在前天我们才终于从这快乐的日子里恢复过来,然后看了两节课,也就是上篇博客有关生产消费模型相关的知识,从中我发现代码能力极具下降,对于很多C++语法感到非常陌生,哈哈哈!所以学习起来非常的痛苦,不过在我不断的回顾和摸索中,感觉自己得到了升华,对许多语法的掌握好像又上了一层楼,具体和以前比较掌握的怎样我也不清楚,谁叫我们生而为人呢?很多东西学的快忘的也快,现在又不是复习的时候,那么就只能在前进的道路上顺便复习一下,哎!不过好在现在有AI,想要搞懂某个问题需要的成本还是非常低的,也是因为有AI的存在,我对我学过的知识可以说是搞懂的较为彻底,我愿奉之为21世纪最伟大的发明,当然我说的是国外的ChatGPT,反之国内真的惨不忍睹。谈到这里,我真的要吐糟一下通义千问,xxxx,用这东西说实话还不如百度,当然文心一言由于没用过,具体不好说,但是估计也差不多。好了废话不多,为了能够早点更文,正式进入该篇博客的主题,有关信号量相关的知识,当然本质还是多线程相关知识。

在这里插入图片描述

回顾生产消费模型

在学习新知识之前,我们先将上篇博客中有关生产消费模型的知识给回顾一下,因为上篇博客由于时间原因,生产消费模型讲解的并不够详细,谁让生产消费模型目前对于我们来说非常重要呢?当然对于以后的我们来说,该知识也是非常重要的,所以这块知识值的我们继续花费时间。

在上篇博客中,我们重点把为什么有生产消费模型和生产消费模型的三种关系,以及生产消费模型的优点给介绍了,并且重点介绍了三种关系之间的关联(互斥、同步),因为这块知识涉及我们在使用代码自己实现该模型时的思路,并且最终依据对应的知识将生产消费模型的代码自我实现了一份。所以此时我们知道,对于生产消费模型来说,首先它要有一个缓冲区,也就是一个容器,可以便于线程读取或者写入数据,其次是要有两种不同的线程,一种执行生产过程,一种执行消费过程,最后要让这两种不同的线程,也就是生产者和消费者满足它们之间的三种关系(生产者和生产者互斥、消费者和消费者互斥、生产者和消费者互斥且同步)。注意: 具体为什么生产者和消费者需要是同步且互斥的关系之前我们讲过,从反例来看,如果允许生产者和消费者同时进行,也即是多线程之间一个线程读取一个线程写入,那么此时就会导致当某个读取线程没有对应的数据可读时,它就必须阻塞在哪里等待写入线程写入,也就是违背了生产消费模型的初衷,没有很好的实现不同线程之间的解耦。所以在生产消费模型中,我们就一定需要让生产者和消费者保持同步状态,也就是当缓冲区中有数据时,才允许通知读取线程来读数据,没数据时,在写入线程写入的同时,让读取线程可以去申请别的共享资源,而不是阻塞。在此原理之上,互斥关系随之产生。

从代码分析生产消费模型

对上述知识又有了一定理解之后,此时我们就从代码来分析一下上述我们说的一个缓冲区,两种线程,三种关系。当然我们此时是以最简单的生产消费模型下手,在缓冲区中我们并没有对不同区域进行同步,而是对整体进行同步,并且我们明白,对于生产消费模型来说它不局限任何类型的数据,所以对于存储数据的地方来说,我们使用的是模板类型。

1.首先是一个缓冲区
同理上篇博客中所说,此时我们的生产消费模型是基于BlockQueue队列作为缓冲区实现的,具体代码如下图所示:
在这里插入图片描述
2.其次是两种不同的线程
搞定了上述生产消费模型中缓冲区相关的知识,其余知识就较为容易,本质就是在利用该缓冲区进行数据的写入和读取而已,所以此时我们就来看看生产者和消费者具体是如何对BlockQueue进行数据的读取和写入吧!具体代码如下所示:
在这里插入图片描述
当然由于我们在BlockQueue(缓冲区)中使用了互斥锁进行同步机制保护,所以我们的程序并不在意访问共享资源(缓冲区)的线程数量,并且明白,因为上述consumer接口和productor接口除了BlockQueue提供的接口之外都是局部变量,所以默认是可重入函数,所以如果多个线程同时对其进行访问,此时也不会引发线程安全问题,单线程访问到多线程访问以及代码执行结果代码如下所示:
在这里插入图片描述
最后一个知识点,当然在上述代码中,肯定还会存在其它七七八八的问题没有讲解,但是身为优质博主,责无旁贷,只选那种非常不好理解的知识点进行重点讲解,如:此时对于多生产多消费过程,在循环创建线程时,这些线程具体是如何运行的,这个知识点我没有记错的话应该在之前讲解pthread_create接口的使用方式上,我们有进行过一定的理解,现在我们再来好好理解一下:重点在于明白单核和多核的区别,我们都知道多线程之间是允许并行访问的,但是这个并行对于单核来说并不是真实的并行,因为每个线程的执行需要经过调度器的调度,调度器再根据线程的优先级去优先调度优先级高的线程,我们也把这个过程称为时间片轮转调度,所以对于单核执行过程来说,线程之间不是并行执行,而是并发执行,当然并发执行指的也就是多个线程在同一时间段内交替执行,通过快速的切换来模拟同时执行的效果。 也就是说,如果你是在单核条件下,那么上述循环创建线程的执行过程就是依次将所有(20)线程创建完成(操作系统完成),只有当全部线程被创建完成之后,调度器才会根据每个线程的优先级不同进行时间片轮转调度,让它们执行相应的代码,也就是我们上述的consumer/productor接口。明白了单核的线程循环创建过程之后,此时多核同理多个单核,也就是线程之间的执行是地地道道的并行执行,不再是并发执行,所以此时在循环创建线程时,就有可能出现多个线程同时被创建,此时就不需要等待其它线程创建完成(因为多核并行完成),此时就可以根据优先级被调度器直接调度执行了。

3.最后是两种不同线程之间的三种关系
明白了上述两种不同线程和单线程多线程的知识,此时我们对生产消费模型中大部分的硬核知识以及误区都给重点强调了一遍,来到这里我们可以说是无所不能,并且由于生产者和生产者、消费者和消费者、生产者和消费者之间的三种关系我们已经在一个缓冲区中相关代码的实现中体现的淋漓尽致了,所以这块知识没什么好讲的,本质就是通过三种关系通过自己对缓冲区设计,构建出一个在生产消费模型逻辑范围内的属于自己的生产消费模型,具体玩的多花由你自己决定。

深入信号量相关知识

搞定了上篇博客有关生产消费模型的知识,此时我们正式进入该篇博客的重点,有关线程同步的最后一个知识点信号量,在深入学习信号量之前,我们要意识到在很久以前学习有关进程间通信相关知识时,无论是在学习匿名管道、命名管道,还是共享内存,本质我们都是在让两个进程看到“同一份资源”,而当我们谈到这同一份资源时我们很容易就能联想到目前学习有关线程的知识。所以同理,当时我们在学习进程间通信了解信号量的本质就是为了让信号量去保护进程间看到的那一份共享资源,只是当时我们没有线程的概念、没有同步与互斥的概念,只知道想让两个进程看到同一份资源的前提是让它们先看到同一份信号量资源,同理当时我们举过的购买电影票的实例(对共享资源进行预定)。而现在当我们有了线程、线程安全的概念,有了并行访问、竞态条件的概念,此时我们就知道共享资源是需要受到保护的,否则就会引发线程安全问题,此时我们对信号量的理解就是一个保护共享资源的同步机制。你想要访问某共享资源,前提是你获取到了相应的信号量,只有获取到了信号量,才允许你进行下一步的操作,否则阻塞。

为什么学习信号量?
明白了上述有关知识之后,也就明白了之前学习的信号量是什么和为什么,以及现在学习到信号量是什么,所以我们就有了很强的前后关联性,从而让我们可以更好的深入相关知识。所以此时我们就来谈谈为什么还要深入信号量?首先我们知道信号量是一个用于描述临界资源中资源数量的计数器,它可以通过PV操作来实现对临界资源中可用资源数量的控制。同理上述所说,当我们想要访问某临界资源时,只有获取到了信号量资源,才允许我们进行下一步操作,通过这一特征我们不难发现,信号量的使用和我们使用互斥锁是一样的,只有当某个线程抢到了互斥锁之后,该线程才有访问临界资源的能力,那么我们为什么还需要学习信号量呢?直接使用互斥锁来实现这一原理不就行了吗?原因和我们之前在谈有关生产消费模型时同理,因为对于一份共享资源我不一定需要设置互斥机制,我可以让所有线程同时访问该共享资源来极大的增加吞吐量和提高线程并发效率(同理超市不是只允许一个人逛),所以此时我可以将共享资源通过加锁和条件变量,反正就是用同步机制的方式分为一个一个不同的区域,当所有线程在访问该共享资源时,每一个区域中的数据因为被同步机制保护,同时只允许一个线程访问,所以在这样的控制方式下,我们的程序不仅不会出现线程安全问题,而且可以大幅度的提高该临界资源的吞吐量和线程的并发执行效率。所以为了实现这一目的,此时就需要有一个计数器来记录当前共享资源中可用资源的数量,也就是我们上述所说的信号量,所以像互斥锁原理那样的信号量被我们称为二元信号量(1->P->0->V->1),反之称为多元信号量(n->P->n-1->P/V->n-2/n)

注意: 因为信号量也是共享资源,同理需要通过原子性实现。并且明白,当一个信号量被申请成功,那么就表示共享资源中一定有某块临界资源已经预定给我使用了(买票原理),所以信号量的预定机制本质就是把判断某临界资源是否可用转换为申请信号量是否成功。 所以在以后的多线程环境编码中,我们就可以直接使用信号量的方法来代替对临界资源是否可用的判断条件,具体有待通过具体场景和代码分析。

信号量相关接口认识

在以前学习信号量的过程中,我们也对信号量相关接口进行了一定的了解,但是注意,当时我们了解的是SystemV标准,而今天我们学习的是POSIX标准,无论是SystemV标准还是POSIX标准,它们本质都只是操作系统中用于进程间通信和同步的两个标准接口,这两个版本具体有什么区别,可参考该篇博客:SystemV和POSIX简介和对比 反正本质就是通信界大佬们在早些年定下的两个标准。

1.SystemV标准信号量
其中在以前学习SystemV标准时,了解的接口有semget()、semctl()、semop(),按照先后顺序它们的作用分别是创建信号量、删除信号量和对信号量加减。具体如何使用可回顾之前学习信号量时的博客:之前的信号量博客

2.POSIX标准信号量
明白了上述知识之后,我们来看看POSIX标准下的信号量接口,此时可分为初始化信号量、销毁信号量和等待信号量,具体使用形式如下所示:

  • 初始化信号量:int sem_init(sem_t* sem,int pshared,unsigned int value); 第一个参数同理互斥锁和条件变量表示信号量参数的地址,第二个参数是一个区分进程间通信和线程间通信的参数(0表示线程,非0表示进程),第三个参数表示信号量的初始值,也就是该共享资源中可用资源的数量。
  • 等待信号量:int sem_wait(sem_t* sem); 该接口起到P操作,也就是对信号量进行减减操作,同理,要等待信号量的地址。
  • 发布信号量:int sem_post(sem_t* sem);同理,该接口起到V的操作,也就是对信号量进行加加的操作,参数同理信号量的地址。
  • 销毁信号量:int sem_destroy(sem_t* sem); 同理,要销毁信号量的地址。

基于环形队列的CP问题

搞定了上述有关信号量的知识,此时我们就需要进入实操阶段,也就是在具体的场景中使用信号量接口来完成对一份共享资源中资源数量的控制,本质也就是使用代码来维护我们自己的线程规则。当然因为时间原因,所以这部分知识我们留到下篇博客再见把!

总结:有关信号量相关的知识,本质就是想让我们见识更加高级的CP(生产消费模型)知识,从而彻底的将多线程相关知识给搞定,See you!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

真机搭建中小网络

这是b站上的一个视频,演示了如何搭建一个典型的中小网络,供企业使用 一、上行端口:上行端口就是连接汇聚或者核心层的口,或者是出广域网互联网的口。也可理解成上传数据的端口。 二、下行端口:连接数据线进行下载的端…

Scratch Blocks自定义组件之「旋律播放」

一、背景 看到microbit edit有旋律编辑器,就在scratch块中也写了一个,如下图所示 这是我写的 这是Micro:bit的 二、功能配置说明 支持8个音符8拍旋律控制 三、使用说明 (1)引入添加field_tone.js到core文件夹中,代码在…

信息系统网络安全整改方案

第1章 项目概述 1.1 项目目标 本方案将通过对公司网络信息系统的安全现状进行分析工作,参照国家信息系统等级保护要求,找出信息系统与安全等级保护要求之间的差距,给出相应的整改意见,推动 XX 企业公司网络信息系统安全整改工作的…

计算机毕设 深度学习手势识别 - yolo python opencv cnn 机器视觉

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…

与“云”共舞,联想凌拓的新科技与新突破

伴随着数字经济的高速发展,IT信息技术在数字中国建设中起到的驱动和支撑作用也愈发凸显。特别是2023年人工智能和ChatGPT在全球的持续火爆,更是为整个IT产业注入了澎湃动力。那么面对日新月异的IT信息技术,再结合疫情之后截然不同的经济环境和…

【抖音小游戏】 Unity制作抖音小游戏方案 最新完整详细教程来袭【持续更新】

前言 【抖音小游戏】 Unity制作抖音小游戏方案 最新完整详细教程来袭【持续更新】一、相关准备工作1.1 用到的相关网址1.2 注册字节开发者后台账号 二、相关集成工作2.1 下载需要的集成资源2.2 安装StarkSDK和starksdk-unity-tools工具包2.3 搭建测试场景 三、构建发布3.1 发布…

【深度学习】MAT: Mask-Aware Transformer for Large Hole Image Inpainting

论文:https://arxiv.org/abs/2203.15270 代码:https://github.com/fenglinglwb/MAT 文章目录 AbstractIntroductionRelated WorkMethod总体架构卷积头Transformer主体Adjusted Transformer Block Multi-Head Contextual Attention Style Manipulation Mo…

探索Vue组件通信的秘密:打破隔阂,实现数据共享

一、Vue组件通信 每个组件都有自己的数据, 提供在data中, 每个组件的数据是独立的, 组件数据无法互相直接访问 (合理的)但是如果需要跨组件访问数据, 就需要用到组件通信 要是有一万个商品????就要写一万个吗?函数调用…

KubeSphere 3.4.0 发布:支持 K8s v1.26

2023 年 07 月 26 日,KubeSphere 开源社区激动地向大家宣布,KubeSphere 3.4.0 正式发布! 让我们先简单回顾下之前三个大版本的主要变化: KubeSphere 3.1.0 新增了“边缘计算”、“计量计费” 等功能,将 Kubernetes 从…

myeclipse的Debug模式

1.表示当前实现继续运行直到下一个断点,快捷键为F8。 2.表示打断整个进程 3.表示进入当前方法,快捷键为F5。 4.表示运行下一行代码,快捷键为F6。 5.表示退出当前方法,返回到调用层,快捷键为F7。 6.表示当前线程的…

kotlin 编写一个简单的天气预报app(五)增加forcast接口并显示

参考资料 OpenWeatherMap提供了一个/forecast接口,用于获取未来几天的天气预报。你可以使用HTTP GET请求访问该接口,并根据你所在的城市或地理坐标获取相应的天气数据。 以下是一个示例请求的URL和一些常用的参数: URL: http://api.openwe…

我的创作纪念日——256天

机缘 最开始我写博客没有什么特别的原因,主要是因为以下几点: 练习自己的语言组织能力 记录自己学习生活中学到的知识 为和我同一个学习阶段的朋友提供帮助 事实上最开始我根本不指望我的博客有多少人看,主要是想找一个好的保存 Markdown 笔…

花费7元训练自己的GPT 2模型

在上一篇博客中,我介绍了用Tensorflow来重现GPT 1的模型和训练的过程。这次我打算用Pytorch来重现GPT 2的模型并从头进行训练。 GPT 2的模型相比GPT 1的改进并不多,主要在以下方面: 1. GPT 2把layer normalization放在每个decoder block的前…

PHP最简单自定义自己的框架(一)

为啥要定义自己的框架: 定制化需求:每个项目都有不同的需求和特点,使用通用的框架可能无法满足所有的要求。自定义框架可以根据具体需求进行定制,提供更加灵活和符合项目需求的解决方案。学习和成长:自定义框架是一个很…

STM32存储左右互搏 I2C总线读写EEPROM ZD24C1MA

STM32存储左右互搏 I2C总线读写EEPROM ZD24C1MA 在较低容量存储领域,EEPROM是常用的存储介质,不同容量的EEPROM的地址对应位数不同,在发送字节的格式上有所区别。EEPROM是非快速访问存储,因为EEPROM按页进行组织,在连…

一文搞懂Redis架构演化之路

目录 从最简单的开始:单机版 Redis 数据持久化:有备无患 主从复制:多副本 哨兵:故障自动切换 分片集群:横向扩展 总结 这篇文章我想和你聊一聊 Redis 的架构演化之路。 现如今 Redis 变得越来越流行,…

图为科技加入深圳市智能交通行业协会 ,打 …

图为科技加入深圳市智能交通行业协会,打造智能交通新生态! 交通是国民经济发展的“大动脉”,交通拥堵、事故频发等问题不仅影响了人们的出行体验,也对经济的发展产生了负面影响。安全、高效、便捷的出行,一直是人们的…

【Unity实用插件篇】| 学会使用 可编程瓦片Tile Map,快速搭建2D地图

前言【Unity 实用插件篇】| 学会使用 可编程瓦片Tile Map,快速搭建2D地图一、导入 Tile Map Editor二、创建调色板 Tile Palette三、快速绘制地图四、TilePalette 调色板功能介绍五、TileMap 相关组件属性介绍GirdTilemapTilemap Renderer 瓦片地图渲染器Tile Assets 瓦片资源…

【Git】分支管理策略

文章目录 分支策略bug分支-master分支出现bug怎么办删除临时分⽀小结 分支策略 在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理: 1.master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯⼲活 2.⼲活都在dev…

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】 This note records how the author begin to learn RL. Both theoretical understanding and code practice are presented. Many material are referenced such as ZhaoShiyu’s Mathematical Foundation of Rei…