学习系统编程No.32【线程互斥实战】

news2024/11/27 17:33:04

引言:

北京时间:2023/7/19/15:22,昨天更新完博客,和舍友下了一会棋,快乐就是这么简单,哈哈哈!总体来说,摆烂程度得到一定的改善,想要达到以前的水准,需要一定的契机,毕竟人生在世,快乐最重要是吧!更文带给我的快乐已经没有那么多了,虽然欠了非常多的作业,非常多的课需要补,很多的题等着我去刷,怎叹一个懒字了得,本质还是作息控制不住,哎!这周小目标更文4篇,只要能达到这个水准,其它的都好说,想到还有那么多课没有看,现在真的挺头疼!不管那么多,正式进入该篇博客的正题,承接上篇博客有关多线程互斥和同步相关的知识,该篇博客我们继续深入理解一下有关线程的互斥和同步吧!

在这里插入图片描述

深入线程互斥

承接上篇博客有关线程互斥相关知识,此时我们在深入理解一下线程互斥。在上篇博客中,我们重点强调了为什么要进行线程互斥和如何进行线程互斥,也就是如何让一份共享资源变为临界资源,每次访问共享资源时,只能有一个线程获得资源的使用权(加锁),其他线程必须等待,直到该线程释放资源后才能继续执行(解锁)。并且在此基础上,我们还简单介绍了有关线程互斥的相关线程库接口,如:pthread_mutex_init,pthread_mutex_lock,pthread_mutex_unlock,pthread_mutex_destroy ,当然我们也明白,在使用这些线程互斥接口的前提是我们定义了一个全局的锁,pthread_mutex_t mutex; 在使用文档中,当我们定义了一个全局的锁结构时,该锁结构是一定需要进行初始化和销毁,也就是必须使用pthread_mutex_init接口和pthread_mutex_destroy,但是使用文档中也给我们提供了另一种方法,让我们可以不需要使用这两个接口,就能完成锁结构的初始化和销毁,在定义锁结构时直接在其后面添加对应的宏结构,pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;这样就可以直接在定义锁的同时,完成对锁的初始化和最终锁的销毁,更简便的供给我们使用。那么此时有的同学就会有问题了,有了这个宏定义,还需要之前初始化和销毁的接口干嘛呢?答案是,使用宏定义快速对锁进行初始化的前提是该锁是一个全局变量的锁,只有是全局变量的锁才有资格使用pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;方法,因为如果对应的锁是局部变量,那么就会导致当函数调用完之后,对应的局部变量锁随着栈帧的销毁而销毁,最终导致编译器无法根据地址再找到对应的锁结构,造成资源泄露问题,所以当定义一个局部变量的锁时,此时就只能使用pthread_mutex_init接口和pthread_mutex_destroy接口进行对应的初始化和销毁。当然此时还没有讲清楚,为什么局部变量不能用 PTHREAD_MUTEX_INITIALIZER方法,只有全局变量可以,这是因为如果使用了 PTHREAD_MUTEX_INITIALIZER方法,本质是将对应的锁变量初始化为默认值,并且将其存储在静态区中,使其生命周期与该进程的声明周期相同,当进程结束,操作系统就会自动回收该锁变量占用的资源,达到销毁目的,所以这也就是为什么局部变量锁不能使用 PTHREAD_MUTEX_INITIALIZER方法的原因。

互斥锁细节介绍
搞定了上述知识,此时我们对锁就有了进一步的理解,当然想要彻底搞定锁相关的知识,重点是搞定锁的实现原理,明白它为什么可以让共享资源变成临界资源,不过在了解锁实现原理之前,此时我们先来谈谈有关加锁方面的细节知识,如:一个共享资源只允许使用一把锁进行保护,不然就有可能导致死锁问题。并且在进行加锁时,加锁的位置一定要合理,尽量细化,只要将共享资源保护起来就行,不允许大范围加锁,否则会导致代码执行效率非常低。然后还要明白,对于锁来说,加锁和解锁本身就是一个原子结构,也就是因为锁也属于共享资源,如果不对锁进行保护的话,那么同理会导致竞态条件问题,所以锁的设计者在设计锁的时候,已经将锁设计为原子结构,也就是当一个线程在访问一个锁的时候,别的线程不允许访问该锁。最后还要明白,一个临界区不仅仅只是一行代码,也可能是一批代码,所以当某个线程在执行该临界区中的代码时,该线程有可能会因为时间片到了,而被操作系统调度,使得对应临界区中的代码没有执行完,但是这并不会导致其它线程可以访问对应的临界资源,因为加锁之后,无论临界区的代码是否执行完毕,其它线程都无法访问到对应的临界资源,这也正是加锁之后带来的线程串行化表现。

锁的基本实现原理
搞定了上述知识,我们正式进入锁的实现原理讲解,首先明白,锁本质就是一个互斥量,因为其可以保护共享资源,也就是只让一个线程访问对应的资源,所以我们将这种特性(互斥)称之为锁,也叫互斥锁。明白了这点之后,接下来我们要搞定的也就是互斥量如何进行互斥,从而让多个线程访问共享资源时,只有一个线程能够成功访问,如下图所示:

在这里插入图片描述

如上图所示,此时我们明白,对于所有线程来说,它们在同时访问同一份共享资源时,因为我们进行了加锁操作(pthread_mutex_lock),所以它们在访问该共享资源之前就需要执行加锁接口相关的代码,也就是如上图所示的伪代码,首先它们要将自己上下文中的%al变量初始化为0,然后再使用exchange接口将我们事先定义并且初始化好的锁变量(mutex)从内存中交换到寄存器,也就是交换到自己的上下文中,让%al由0变1,让mutex由1变0,完成了这一步骤之后,对应的线程就实现了互斥操作,也就是我们所说的加锁,并且此时mutex就是该互斥操作中的互斥量。交换的目的就是让这个互斥量只被一个线程拿到,当下一个线程也要交换时,由于mutex互斥量已经变为了锁定状态(0),此时它就无法获取到mutex中的1(未锁定状态),从而无法执行pthread_mutex_lock中的后序代码,只能被操作系统挂起等待(if语句判断)。最后明白一点,也就是我们一直说的锁是共享资源,却不会造成竞态条件的原因是因为其设计成了原子性,从上图我们就能看出,一个线程在执行对应加锁代码时,其中获取互斥量的过程,仅仅就只是一个交换语句,所以可以明白,对于线程来说,单独一句代码,要么执行,要么就是不执行,所以对于加锁操作,它天生就是原子性的。

所以同理解锁操作,就是将mutex的值由0变1,这样,下一个线程在执行加锁操作时,就可以获取到对应mutex互斥量中的1,因为此时mutex处于未锁定状态(1),同理获取到之后(交换)mutex就又会处于锁定状态(0),所以这也是为什么有加锁操作,就一定要有解锁操作,否则就会造成死锁,无论是那个线程都无法访问到该共享资源。

线程封装

明白了上述有关互斥锁的相关知识之后,此时我们进行线程的封装,也就是对pthread.h头文件中有关线程控制相关接口的封装,实现一个自己的简易线程库,当然无论是在C++,还是Java中,它们的线程库都是和我们一样,对pthread.h头文件进行的封装,只不过在设计上不同,所以导致不同的语言在线程库的使用上不同,本质原因就是封装的方法不同,如下代码所示,就是我们自己对线程库的一个封装:

在这里插入图片描述

如上图所示,此时我们就使用Thread类,完成了对线程库的一个简易封装,重点就是注意参数类型和函数指针传参方面的问题,并且还要注意有关静态成员函数相关的知识,也就是如果在一个类中,你因为参数的原因,无法使用this指针,那么此时你就可以使用静态成员函数,使用static声明,这样就完成了该成员函数和类之间的解耦,但是,因为你将该成员函数和类解耦,所以也就导致该成员函数没有this指针,最终导致该成员函数无法访问到类中的成员变量,具体如何解耦这里我们不详谈,这里注意,明白会用就行。

互斥锁的封装

明白了上述有关线程库的封装,此时我们再来看看有关互斥锁的封装,当然此时的封装还是同理对系统接口进行封装,而不是对伪代码进行封装,本质就是为了让我们可以更方便的使用加锁和解锁,如下代码所示:

在这里插入图片描述

此时我们就完成了对锁的封装,那么此时有的同学就会问了,为什么要这样对锁进行封装呢?如下代码所示:本质就是为了让加锁和解锁操作变得更加简易

在这里插入图片描述
如上图所示,通过两种不同的加锁和解锁操作,我们发现,如果将加锁和解锁操作封装在一个类的构造和析构函数中,然后通过对该类对象进行传参,这样可以非常方便的完成对共享资源的保护。

总结:有关线程互斥,互斥锁的基本原理和封装,有关线程等相关知识我们就讲到这里啦!更多有关线程互斥与同步的知识,我们下篇博客见。

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

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

相关文章

分布式 - 消息队列Kafka:Kafka分区常见问题总结

文章目录 01. Kafka 的分区是什么?02. Kafka 为什么需要分区?03. Kafka 分区有什么作用?03. Kafka 为什么使用分区的概念而不是直接使用多个主题呢?04. Kafka 分区的数量有什么限制?05. Kafka 分区的副本有什么作用&am…

[内网渗透]XXE-vulnhub

文章目录 [内网渗透]XXE-vulnhub环境安装信息收集解题步骤总结 [内网渗透]XXE-vulnhub 环境安装 首先在 vulnhub 下载文件: 将其解压为ovf格式: 使用vmware打开,新建一个虚拟机,然后开机,环境就搭好了 信息收集 由于…

【仿写spring】一、通过反射读取带有@RequestMapping与@Controller注解的类并模拟请求路径调用方法

目录 简介思路实践一、自定义注解RequestMapping,Controller二、路径转全限定名方法三、扫描文件夹四、通过反射来寻找有RequestMapping以及Controller的类五、获取对象实例六、通过invoke调用方法 文件结构以及测试结果1、文件结构2、TestController3、测试结果 简…

uniapp 条件编译失败,跑不起来

因为这行代码整个uniapp都跑不起来&#xff0c;谁懂救命。再说uniapp的异常提示也太反人类了<!-- <image :src"require(/ baseListItem.url)" /> -->

Linux进程控制(三)---进程替换+简易shell的实现

目录 execl() execv() execlp() execvp() 如何利用execl执行自己写的C/C可执行程序&#xff1f; 如何利用makefile同时编译两个文件 execle() execvpe() 简单shell的编写 什么是进程替换&#xff1f; 我们之前fork之后&#xff0c;是父子进程各自执行代码的一部分&am…

【人工智能】大模型平台新贵——文心千帆

个人主页&#xff1a;【&#x1f60a;个人主页】 &#x1f31e;热爱编程&#xff0c;热爱生活&#x1f31e; 文章目录 前言大模型平台文心千帆发布会推理能力模型微调 作用 前言 在不久的之前我们曾讨论过在ChatGPT爆火的大环境下&#xff0c;百度推出的“中国版ChatGPT”—文…

深度神经网络基础——深度学习神经网络基础 Tensorflow在深度学习的应用

目录 一、二、Tesnsflow入门 & 环境配置 & 认识Tensorflow三、线程与队列与IO操作神经网络基础知识1.简单神经网络2.卷积神经网络卷积层新的激活函数-Relu池化层(Pooling)计算 案例&#xff1a;Mnist手写数字图片识别卷积网络案例 一、二、Tesnsflow入门 & 环境配置…

IDEA无法链接代理The driver has not received any packets from the server.

问题如下: 1、本地部署Proxifier,且设置全局代理,截图如下 代理工具 2、通过Navicat 工具连接该远程数据库,连接正常,截图如下 3、通过IDEA或者Eclipse连接(通过代理),抛连接失败(数据库地址绝对无误),如果把数据库地址改成本地的mysql地址,可以正常连接(不用通过代理)…

mac使用mvn下载node-sass 会Binary download failed, trying source

m1 上使用nvm 以下node的版本可以直接下载&#xff08;Binary download&#xff0c;而不是 trying source&#xff09;而不用切换mac cpu架构 zhiwenwenzhiwenwendeMBP cockpit % nvm install 14.15.5 Downloading and installing node v14.15.5... Downloading https://node…

二十五章:TransUNet:Transformer为医学图像分割提供强大的编码器

0.摘要 医学图像分割是发展医疗系统的重要先决条件&#xff0c;特别是对于疾病诊断和治疗计划。在各种医学图像分割任务中&#xff0c;U型架构&#xff0c;也称为U-Net&#xff0c;已成为事实上的标准&#xff0c;并取得了巨大的成功。然而&#xff0c;由于卷积操作的固有局部性…

Zabbix监控linux主机(agent端)

目录 一、Linux-clinet操作&#xff08;agent&#xff09; 二、源码安装zabbix 三、Zabbix添加linux主机 为agent.zabbix.com添加模板 等待一会 查看效果如下 一、Linux-clinet操作&#xff08;agent&#xff09; [rootlocalhost ~]# ifconfig ens33[rootlocalhost ~]# vim…

免费的游戏图标素材库分享

游戏图标设计在游戏UI中占有非常重要的地位。例如&#xff0c;当我们看到一个游戏的启动图标时&#xff0c;很容易区分它是哪个游戏。设计游戏图标不仅是一个图形&#xff0c;也是一个标志。 本文将通过各种游戏图标设计素材分享游戏图标的类别和设计游戏图标的思考。 1. 游戏…

程序员基础知识—IP地址

文章目录 一、什么是IP地址二、IP地址的分类三、子网掩码 一、什么是IP地址 IP地址就像我们需要打电话时的电话号码一样&#xff0c;它用来标识网络中的一台主机&#xff0c;每台主机至少有一个IP地址&#xff0c;而且这个IP地址是全网唯一的。IP地址由网路号和主机号两部分组…

vue 3.0 如何加载图片

.logo { background: url(~/assets/images/logo.svg) no-repeat center center/contain; width: 117px; height: 24px; margin: 0 20px; } <a class"logo" href"#"></a> 比较实用的书写方式

小程序制作教程

步骤一&#xff1a;规划和设计 在开始制作微信小程序之前&#xff0c;首先需要规划和设计您的小程序。确定您想要提供的服务或功能&#xff0c;并考虑用户体验和界面设计。绘制草图和构思完整的页面布局&#xff0c;这将使您更好地理解小程序结构和功能。 步骤二&#xff1a;…

Arrays.asList

文章目录 摘要详解我们再去看看 java.util.ArrayList 为什么可变的呢&#xff1f;Arrays.asList()和 Collections.singletonList()额外&#xff1a;Collections.singletonList() 摘要 先总结要点&#xff0c;接下来详细讲解 返回由指定数组支持的长度不可变的列表&#xff0c…

题目3 文件包含(保姆级教程)

url&#xff1a;http://192.168.154.253:83 #打开http://XXX:81/&#xff0c;XXX为靶机的ip地址 审题 1、打开题目看到有一个提示&#xff0c;此题目需要通过利用存在的文件包含漏洞&#xff0c;尝试获取webshell&#xff0c;最后从根目录下key.php文件中获得flag 2、开始答题…

老年公寓人员定位管理系统:提升安全与关怀的智能解决方案

老年公寓作为提供安全居住环境和关怀服务的重要场所&#xff0c;面临着人员管理和安全控制的挑战。为了解决这些问题&#xff0c;老年公寓人员定位管理系统应运而生。基于为提供全面的安全管理和个性化关怀服务&#xff0c;华安联大便通过老年公寓人员定位管理系统的技术原理、…

在react中配置less

第一步&#xff1a;暴露出webpack配置文件 终端命令&#xff1a;npm run eject (此命令一旦运行不可逆) 第二步&#xff1a;安装less以及less-loader npm install less less-loader --save-dev 第三步&#xff1a;修改webpack的配置文件 运行完以上命令后&#xff0c;项目…

文心千帆大模型测评分享,效果超出预期

一、前言 现如今&#xff0c;随着ChatGPT的爆火越来越多的人开始关注人工智能领域了&#xff0c;大家都在尝试使用它来帮助自己在工作上提高效率亦或是解决一些问题。但ChatGPT是有一定的使用门槛的&#xff1a;首先需要我们“科学上网”才能访问&#xff0c;其次GPT4的价格相…