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

【Linux系统】线程互斥与同步

目录

一.几个概念

二.线程互斥

1.定义并初始化锁 

2.加锁 

3.解锁 

4.销毁锁

三.互斥锁的本质 

1.xchg的原子性

2.加锁的过程

3.解锁的过程

四.可重入VS线程安全

五.死锁

1.死锁的概念

 2.具体实例

 3.死锁产生的四个必要条件

4.解决或避免死锁

六.线程同步 

七.生产者消费者模型  

1.概念

2.意义

八.条件变量 

1.定义并初始化条件变量

2.等待条件变量

​编辑

3.唤醒等待条件变量的一个或多个线程

 九.条件变量的细节问题


一.几个概念

  1. 临界资源:任何一个时刻,只允许一个执行流访问的共享资源
  2. 临界区:访问(read/write)临界资源的代码
  3. 互斥:保护临界资源的一种访问方式,即任何时刻,保证只有一个执行流进入临界区,访问临界资源
  4. 原子性:一个操作的性质,该操作不会被调度机制打断,只有未完成和已完成两种状态

C++语言中的--操作会转换为三条汇编语句:

  1. 将内存中的数据mov到CPU的寄存器
  2. 对寄存器中的数据dec(减1)
  3. 将寄存器中的数据写入到内存

一条汇编语句才是原子的,因此a--这句代码不是原子的!!!

 举例:两个线程并发对int a = 10进行++操作。线程1将10从内存拷贝到寄存器,进行+1操作,正要准备写2回到内存时,线程1被切换了,它会将寄存器内的值保存到CPU中。线程2从内存中读取到的还是10,进行若干次++操作,加到了100,并将100写回了内存。此时线程1再次被调度,从PCB中恢复硬件上下文,接着上一次未做完的工作,将寄存器中的11写回到内存。线程2好不容易把a加到100,现在一把回到解放前。出现以上问题的原因是,++操作不是原子的,数据从内存到寄存器,从寄存器到内存的过程没有一次性完成,被线程调度打断了。

内存中的数据是共享的,寄存器中的上下文是线程私有的,数据不一致就是指,上下文数据和内存中的共享数据不一致。

二.线程互斥

数据不一致问题,是因为多线程并发访问临界区,访问临界资源。所以只需保证,任一时刻,只有一个线程访问临界区,即可实现对临界资源的保护,以上的资源保护方式叫做互斥,互斥可以通过对临界区加互斥锁的技术手段来完成。

每个线程访问临界区,都必须先加锁,只有加锁成功,才能访问临界区,否则会阻塞等待。这是大家(线程或者编写代码的用户)都应该遵守的规则,如果有的线程遵守,有的不遵守,那锁便失去了意义。

1.定义并初始化锁 

定义并初始化全局的锁: 

定义并初始化局部的锁: 

2.加锁 

  1. 尽可能给少的代码加锁
  2. 一般给临界区加锁
  3. 访问临界区的线程都必须加锁,大家都应遵守这套规则
  4. 任何时刻,只会有一个线程申请加锁成功
  5. 锁也是临界资源,但申请加锁的操作本身就是原子的,所以申请加锁不会出现数据不一致问题,而导致多个线程申请锁成功。
  6. 一个线程加锁成功后,访问临界区,也会发生线程切换。但其它申请锁的线程会在pthread_mutex_lock上阻塞等待,无法访问临界区。加锁间接实现了线程访问临界区的原子性。
  7. 加锁的一般原则:谁加锁,谁解锁

3.解锁 

4.销毁锁

全局锁无需销毁,局部锁要及时销毁:

三.互斥锁的本质 

1.xchg的原子性

计算机通常会提供xchg汇编指令(硬件支持对应机器码),其作用是将寄存器内容和内存单元的内容进行交换。如果机器不支持exchange指令,那我们用户想要完成同样的工作,只能先将寄存器内容备份到另一个寄存器,再将内存拷贝到寄存器,最后将备份的内容拷贝到内存,分三步完成。但机器支持xchg指令,就只需要一步,这意味这,它是原子的。

2.加锁的过程

可以简单认为互斥锁就是一个值为变量,值为1表示可以被申请,值为0表示被占用

1.将某个寄存器的内容置0

2.使用xchg交换寄存器和mutex的内容->寄存器内容变为mutex的值,mutex变为0

3.判断寄存器内容是否大于0,如果大于0,则成功返回,否则挂起等待

只有第二步涉及到访问内存中的共享资源,而第二步又是原子的,所以整个加锁过程就是原子的。

内存中的mutex是线程共享的,寄存器中的上下文是线程私有的。xchg的本质是将共享资源mutex的数据交换到上下文中,属于线程自己。互斥锁的“1”就像一个令牌,谁拥有了“1”,谁就拥有了锁,即使线程被切换,它也会将上下文保存到PCB中打包带走。

3.解锁的过程

1.将1mov到内存的mutex中

2.唤醒等待mutex的线程(只不过从是从阻塞状态唤醒,可以被CPU调度,不是直接把锁分配给它),并成功返回

  

可以看出,解锁也是原子的

四.可重入VS线程安全

  1. 一个函数被多个执行流进入,称该函数被重入了。如果因为函数被重入而使程序行为不符合预期(崩溃或者数据不一致),称为该函数不可重入;否则,则称该函数可重入
  2. 多线程并发访问共享资源时,程序的行为符合预期,则称该线程是安全的。
  3. 线程安全描述的是线程的特征,可重入描述的是函数的特征。
  4. 多线程调用不可重入函数会导致线程不安全,多线程调用可重入函数不会导致线程不安全

五.死锁

1.死锁的概念

一组进程中的各个进程均占有不会释放的资源,但因为相互申请被其他进程所占用不会释放的资源,而处于的一种永久等待的状态。简而言之,死锁就是线程等待一个永远都得不到的锁

 2.具体实例

实例1:现有某个共享资源,规定线程访问它需要持有锁1和锁2。线程A欲要访问该资源,已经持有锁1,正在申请锁2;线程B也想访问该资源,已经持有锁2,正在申请锁1。线程A和线程B都无法集齐两把锁,也都不放弃手中的锁,导致线程A和线程B都被阻塞挂起,永远不会被调度。

以上是两把锁造成的死锁,实际上一把锁也能造成死锁

实例2:由于用户的粗心,将解锁代码写成加锁,导致线程等待自己手中的锁,但它永远等不到

 3.死锁产生的四个必要条件

  1. 互斥条件:使用锁以达到访问共享资源互斥
  2. 请求与保持条件:线程在申请锁的同时,对已获得的锁保持不放
  3. 不剥夺条件:一个线程手中的锁在使用完之前,不会被其它线程强行释放
  4. 循环等待条件:A找B要,B找A要

4.解决或避免死锁

破坏产生死锁的4个条件之一即可:

  1. 破坏互斥条件。使用锁是因为要保护共享资源,在条件允许的情况下,让每个线程私有一份资源,这样就不必使用锁来保证互斥了
  2. 破坏请求与保持条件。如果一个线程申请锁不成功,就把持有的锁都释放掉
  3. 破坏不剥夺条件。线程A想申请线程线程B手中的,管理线程检测到死锁,强行将线程B的锁释放
  4. 破坏循环等待条件。如果多线程要申请相同的锁,建议按照同样的次序申请

用户编码建议:

  1. 能不用锁就不用锁
  2. 如果要线程需要申请多个锁,尽量按照顺序一次性申请到位,不要分批申请

六.线程同步 

同步:在临界资源使用安全的前提下,让多线程执行具有一定的顺序性,以保证合理使用资源,解决线程饥饿问题。线程同步使用条件变量完成。

互斥能保证资源的安全,同步让资源使用更加高效。

七.生产者消费者模型  

1.概念

生产者消费者模型是多线程并发的一种经典设计模式。分为两种角色:

生产者:产生数据的线程,将产生的数据放置到共享的内存空间

消费者:从共享的内存空间取数据进行处理

生产者和消费者之间的关系如下:

生产者和生产者:互斥。同时向一块内存写入,会出现数据不一致问题(覆盖或混乱)

消费者和消费者:互斥。同时从一块内存读数据,会出现数据不一致问题(混乱)

生产者和消费者:互斥和同步。同时写和读,会导致消费者得到的数据残缺不全,所以需要互斥;生产者生产完成后再通知消费者来取,避免消费者做一些浪费资源的行为,所以需要同步。

注意:生产是指将数据放到交易场所,消费是指从交易场所拿走数据

2.意义

我们调用函数的目的,就是让函数作为消费者,处理传递给它的数据。但是如果函数的处理过程没有完成,调用处的代码就无法向下执行。

  1. 实现生产过程和消费过程执行解耦。共享的内存空间相当于一块缓存,只要缓存没有打满或者缓存不为空,消费和生产过程就不会相互影响,支持生产消费忙闲不均
  2. 提高数据处理的效率。生产者生产数据,前提是要获取数据,消费者取走数据后,还要进行处理。消费者的数据一般从具体场景中得来,例如从网络读取,也就是说,获取数据本身也要花费时间,消费者处理数据也要花时间。多生产和多消费模型在任何一个时刻只允许一个线程进入,高效并不体现在同步和互斥,而是使得获取数据和处理数据更好地并发。

八.条件变量 

1.定义并初始化条件变量

2.等待条件变量

3.唤醒等待条件变量的一个或多个线程

 前者是唤醒所有的线程,或者是唤醒队头的线程

 九.条件变量的细节问题

  1. 在加锁和解锁之间要往往是要访问临界资源,但资源不一定是就绪的,所以需要有if语句这样的判断。如果判断条件不满足,什么也不做,线程会再次执行加锁,判断,解锁这样的无效工作。为了避免资源浪费,在条件不满足时,让线程在条件变量上阻塞等待。
  2. 让线程进行等待的时候,会自动释放锁
  3. 线程被唤醒的时候,是在临界区内唤醒的,当线程被唤醒,线程在pthread_cond_wait返回的时候,要重新申请锁  
  4. 当线程被唤醒的时候,重新申请锁本质是也要参与锁的竞争的

相关文章:

【Linux系统】线程互斥与同步

目录 一.几个概念 二.线程互斥 1.定义并初始化锁 2.加锁 3.解锁 4.销毁锁 三.互斥锁的本质 1.xchg的原子性 2.加锁的过程 3.解锁的过程 四.可重入VS线程安全 五.死锁 1.死锁的概念 2.具体实例 3.死锁产生的四个必要条件 4.解决或避免死锁 六.线程同步 七.…...

武汉星起航引领跨境电商新潮流,深耕亚马逊打造全方位合作新模式

在全球化的浪潮下,跨境电商已成为连接各国市场的重要桥梁,为无数企业带来了前所未有的发展机遇。在这一领域,武汉星起航电子商务有限公司以其独特的战略眼光和实战经验,成为引领行业发展的佼佼者。公司自2017年起便深耕亚马逊平台…...

GateWay路由规则

Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规 则(通过 Header、请求参数等作为条件)匹配到对应的路由 1 时间点后匹配 server:port: 8888 spring:application:name: gateway-servicecloud:nacos:discovery:…...

shell脚本基础改造

一、基础的shell脚本格式 #!/bin/bash 2 #3 #********************************************************************4 #Author: LJH5 #QQ: 2…...

静态综合实验

一,1.搭建拓扑结构并启动。 2.根据题意得该图需要14个网段,根据192.168.1.0/24划分子网段,如下: 划分完如图所示: 二、配置IP地址 R1路由器: 1.进入系统视图并改名. 2.接口配置IP地址&#xff1a…...

Spring Web MVC入门(6)

应用分层 在开发的过程中, 我们会发现, 程序的代码有时会很"杂乱", 如果后面的项目更大了, 那就会更加地杂乱无章(文件乱, 代码内容乱). 也基于此, 接下来让我们来学习一下应用分层. 也类似于公司的组织架构 公司初创阶段, 一个人身兼数职, 既做财务, 又做人事,还有…...

muduo异步日志

muduo异步日志实现 陈硕老师的muduo网络库的异步日志的实现,今晚有点晚了,我明晚再把这个异步日志抽出来,作为一个独立的日志库。 所在文件 AsyncLogging.cc AsyncLogging.h LogFile.h LogFile.cc CountDownLatch.h CountDownLatch.cc…...

在智慧能源的发展历程中,哪些技术的出现起到了关键性的作用?

智慧能源作为一种全新的能源发展理念,正逐渐成为能源领域的热门话题。在智慧能源的发展历程中,有许多技术的出现起到了关键性的作用,推动了智慧能源的快速发展。 一、物联网技术 物联网技术使得能源设备可以实现互联互通,通过传感…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(十三)

返回:SQLite—系列文章目录 上一篇:SQLiteC/C接口详细介绍sqlite3_stmt类(十二) 下一篇: 待续 51、sqlite3_stmt_scanstatus_reset sqlite3_stmt_scanstatus_reset 函数用于重置指定语句对象最近一次执行的 WHER…...

扫雷(蓝桥杯,acwing)

题目描述: 扫雷是一种计算机游戏,在 2020 世纪 80 年代开始流行,并且仍然包含在某些版本的 Microsoft Windows 操作系统中。 在这个问题中,你正在一个矩形网格上玩扫雷游戏。 最初网格内的所有单元格都呈未打开状态。 其中 M个…...

macOS 通过 MacPorts 正确安装 MySQL 同时解决无法连接问题

如果你通过 sudo port install 命令正常安装了 MySQL,再通过 sudo port load 命令启动了 MySQL Server,此刻却发现使用 Navicat 之类的 GUI 软件无法连接,始终返回无法连接到 127.0.0.1 服务器。这是一个小坑,因为他默认使用了 So…...

Semi-supervised Open-World Object Detection

Semi-supervised Open-World Object Detection 摘要1 介绍2.准备工作提出的SS-OWOD问题设置2.1 基础架构3 方法3.1整体架构摘要 传统的开放世界对象检测(OWOD)问题设置首先区分已知和未知类别,然后在后续任务中引入标签时逐步学习未知对象。然而,当前的OWOD公式在增量学习…...

C语言实现射击小游戏

以下是一个简单的C语言射击小游戏的实现示例。这个游戏中&#xff0c;玩家控制一个飞船&#xff0c;敌方飞船会随机出现并向玩家移动。如果玩家的飞船与敌方飞船相撞&#xff0c;玩家就失去一条生命&#xff0c;代码如下&#xff1a; #include <stdio.h> #include <s…...

c++11 标准模板(STL)本地化库 - std::islower(std::locale) 检查字符是否被本地环境分类为小写

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析&#xff0c;以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 检查字符是否被本地环境分类为小写 std::islower(std::locale) template&…...

粘度指数改进剂市场需求增长 为润滑油添加剂细分产品

粘度指数改进剂市场需求增长 为润滑油添加剂细分产品 粘度指数改进剂是一种油溶性高分子聚合物&#xff0c;主要用于提高润滑油粘度以及粘度指数。粘度指数改进剂具有稠化能力强、抗磨性好、热稳定性好等优势&#xff0c;可添加于液压油、内燃机油以及齿轮油等油品中。 …...

LabVIEW柴油机安保监控系统

LabVIEW柴油机安保监控系统 随着航运业的快速发展&#xff0c;确保船舶柴油机的安全稳定运行变得尤为重要。船舶柴油机故障不仅会导致重大的经济损失&#xff0c;还可能危及人员安全和环境。设计并开发了一套基于LabVIEW平台的柴油机安保监控系统&#xff0c;旨在通过实时监控…...

实测国内AI大模型问答效果

随着ChatGPT热度的攀升&#xff0c;越来越多的公司也相继推出了自己的AI大模型。按照github工程awesome-LLMs-In-China所列举的&#xff0c;现如今国内AI大模型已达243个&#xff0c;比较著名的有文心一言、通义千问等。各大应用也开始内置AI玩法&#xff0c;如抖音的AI特效。下…...

不得不等待的无奈 -《葡萄成熟时》

恋上一个人便是撒下一颗葡萄种子&#xff0c;你可能会坚持&#xff0c;但不一定会结果&#xff0c;收获&#xff08;在一起&#xff09;。 更有可能得到的是枯枝烂叶&#xff08;ta的离开&#xff09;。 就算你再努力&#xff0c;再用心去栽培&#xff08;为ta付出&#xff0…...

【Python】Python中装饰器和魔法方法的区别

在Python中&#xff0c;装饰器&#xff08;Decorators&#xff09;和魔法方法&#xff08;Magic Methods&#xff09;是两种不同的高级特性&#xff0c;分别服务于不同的目的。 装饰器 (Decorators) 装饰器是一种强大的工具&#xff0c;它可以修改或增强函数、方法或类的行为…...

【React】创建你的第一个React组件

要使用React创建你的第一个组件&#xff0c;首先确保你已经安装了Node.js和npm&#xff08;Node包管理器&#xff09;。然后&#xff0c;你可以通过npm安装Create React App这个官方支持的脚手架工具来快速生成一个新的React应用项目&#xff0c;该项目包含了React、ReactDOM、…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...