【Linux多线程编程】5. 线程锁(2)——死锁、读写锁

news2025/1/6 20:40:18

前言

上篇文章【Linux多线程编程】4. 线程锁(1)——互斥锁 我们介绍了线程同步的其中一种方式——互斥锁,互斥锁也可以理解为独占锁,只要有一个线程拿到该锁,其他的线程想要获取只能阻塞等待。但互斥锁的使用不当也可能会导致一些问题,比如死锁。本篇文章将介绍死锁以及另一种线程同步方式——读写锁。

死锁

典型死锁问题——银行家死锁

死锁发生在线程争夺锁的过程中,一个比较典型的案例就是银行家死锁问题,银行家死锁的问题的描述如下图所示:
在这里插入图片描述

五个人坐在一起吃饭,但是吃饭需要同时持有刀叉才可以,每个人持有资源如下
1号:持有刀,请求叉
2号:持有叉,请求刀
3号:持有叉,请求刀
4号:持有刀,请求叉
5号:持有叉,请求刀

但是现场只有3把 叉,2把刀;每个人都在请求另一半资源,但每个人都不肯放下自己持有的资源,所以盘子里的饭都没动:)
这是典型的一种死锁情况,死锁的原因就是每个人都持有一半资源,请求另一半资源,但没有人肯放下自己的资源。
有没有办法解决呢?显然,只要其中一个人愿意先放下自己持有的资源,让别人获取到,获取到的那个人就可以吃饭,用完资源后释放,其他的人就可以竞争到资源最后每个人都吃完。核心就是释放已有的资源
另一种方法就是规定资源获取的顺序,如果每个人都先拿刀,再拿叉,就一定有一个人会先同时持有刀叉,然后就可以继续运转起来。核心是规定资源获取的顺序

互斥锁使用不当引起的死锁问题

上面叙述的是正常使用锁情况下,资源竞争不当引发的死锁问题。还有一些不注意时会引发的死锁问题,更为常见,如下场景

  1. 加锁后未释放
  2. 对同一把锁重复加锁

场景一可能是如下的代码:

pthread_mutex_t mutex;
for (int i = 0; i < 100; ++i)
{
    pthread_mutex_lock(&mutex);
 
    //共享资源
    ...
    // 此处没有释放锁
}

场景二可能是如下代码:

pthread_mutex_t mutex;
void* func()
{
    pthread_mutex_lock(&mutex);
    //共享资源
    ...
    // 未释放锁前重复加锁
    pthread_mutex_lock(&mutex); // 重复加锁
}

当然这样的代码很明显,一眼就能看出来,实际开发中,最最可能出现的就是跨函数调用的加解锁,即如下代码示例:

pthread_mutex_t mutex;
void* funcA()
{
    pthread_mutex_lock(&mutex);
    //共享资源
    ...
    pthread_mutex_unlock(&mutex); // 重复加锁
}
void* funcB()
{
    pthread_mutex_lock(&mutex);
    //共享资源
    ...
	funcA(); // 调用 funcA,funcA中再次对 mutex 加锁,造成重复加锁,funcA 加不上锁阻塞,导致死锁
    pthread_mutex_unlock(&mutex); // 重复加锁
}

这段代码,单独看每个函数都没有问题,但是一旦运行起来必定死锁,所以实际开发中要检查函数的调用栈,确保上下级调用不会有重复加锁的问题。

读写锁

读写锁场景

如果多个线程同时访问临界资源的时候,但只对其进行读取的操作,并不进行修改,这个时候使用互斥锁让所有线程排队读取的效率就不高了。

在对数据的读写操作中,更多的是读操作,写操作较少,例如对数据库数据的读写应用。为了满足当前能够允许多个读出,但只允许一个写入的需求,线程提供了读写锁来实现。

就好比在淘宝买东西,一百个人同时想查一下某个商品还有多少库存,这个时候如果一个人正在查的时候其他人就不能查,效率就会很低。

读写锁介绍

读写锁是一种特殊的互斥锁,如果一个线程想要读取临界资源就加读锁,想要修改临界资源就加写锁。

这里分为四种情况:
在这里插入图片描述

线程A想对临界资源X加读锁,X已被其他线程加了读锁,此时线程A加读锁成功
线程A想对临界资源X加读锁,X已被其他线程加了写锁,此时线程A加读锁失败
线程A想对临界资源X加写锁,X已被其他线程加了读锁,此时线程A加读锁失败
线程A想对临界资源X加写锁,X已被其他线程加了写锁,此时线程A加读锁失败

读写锁的使用在下篇进行介绍

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

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

相关文章

【云原生】Ceph 在 k8s中应用

文章目录一、概述二、Ceph Rook 介绍三、通过Rook在k8s中部署Ceph1&#xff09;下载部署包2&#xff09;部署 Rook Operator3&#xff09;创建 Rook Ceph 集群4&#xff09;部署Rook Ceph 工具5&#xff09;部署Ceph Dashboard6&#xff09;检查6&#xff09;通过ceph-tool工具…

VirusTotal智能搜索itw查找从github下载的恶意Android样本

1. Introduction ITW是in the wild的缩写&#xff0c;VirusTotal提供了itw这个搜索关键词&#xff0c;可以搜到从某个url&#xff08;部分url&#xff09;上下载到的样本。 作者写过的其他VirusTotal智能搜索用法的文章见参考1和2. 2. itw使用 比如为了查找从github下载的恶…

Day852.Thread-Per-Message模式 -Java 性能调优实战

Thread-Per-Message模式 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于Thread-Per-Message模式的内容。 Thread-Per-Message 模式&#xff0c;简言之就是为每个任务分配一个独立的线程。 并发编程领域的问题总结为三个核心问题&#xff1a; 分工同步互斥 其中&…

client-go源码学习(三):Indexer、SharedInformer

本文基于Kubernetes v1.22.4版本进行源码学习&#xff0c;对应的client-go版本为v0.22.4 3、Informer机制 4&#xff09;、Indexer Indexer中有Informer维护的指定资源对象的相对于etcd数据的一份本地缓存&#xff0c;可通过该缓存获取资源对象&#xff0c;以减少对Kubernete…

计算Java对象大小(附实际例子分析)

对象大小如何计算 对象大小包括俩部分的内容&#xff0c;对象头和对象内容。&#xff08;图片源于网络&#xff09; 对象头 此处假设是64位的JVM 对象地址&#xff0c;占4个字节。对象标记&#xff0c;占8个字节&#xff0c;包括锁标记&#xff0c;hashcode, age 等。数组…

python 如何使用 pandas 在 flask web 网页中分页显示 csv 文件数据

目录 一、实战场景 二、知识点 python 基础语法 python 文件读写 python 分页 pandas 数据处理 flask web 框架 jinja 模版 三、菜鸟实战 初始化 Flask 框架&#xff0c;设置路由 jinja 模版 渲染列表数据 分页请求数据 显示详情页数据示例 运行结果 运行截图 …

[oeasy]python0040_换行与回车的不同_通用换行符_universal_newlines

换行回车 回忆上次内容 区分概念 terminal终端 主机网络中 最终的 端点 TeleTYpewriter 电传打印机终端硬件 shell 终端硬件基础上的 软件壳子 Console 控制台 主机旁边 的 控制面板 存储文件 的 时候 我 在文件里 打了回车\n系统 将0x0a存入字节 进文件换行 自动就有 回车…

航空客运订票系统(C语言,软件用的DEV)

这两天整理之前的作业代码&#xff0c;把自己一点一点敲出来的系统又看了一下&#xff0c;挑几个发出来供大家参考。想要源码、报告可以找我啦&#xff0c;代码的注释之前写的都是非常详细的&#xff01; 但是不是无偿的啦&#xff08;不坑&#xff0c;一杯奶茶喽&#xff0c;不…

Java逃逸分析(附实际例子分析)

Java逃逸分析 1. 什么是Java逃逸分析 我们知道对象一般是在堆上生成的&#xff0c;但这并不是绝对的。特例就是今天要说的逃逸分析。 JVM 在分析代码以后&#xff0c;发现一个对象在声明之后&#xff0c;只有在它当前声明的这个函数中调用&#xff0c;那么它就会将这个对象在…

《微SaaS创富周刊》第3期:GPT-3\ChatGPT、Stable Diffusion等AI模型驱动的微SaaS创意盘点

大家新年好&#xff01;第3期《微SaaS创富周刊》问世啦&#xff01;本周刊面向独立开发者、早期创业团队&#xff0c;报道他们主要的产品形态——微SaaS如何变现的最新资讯和经验分享等。所谓微SaaS&#xff0c;就是“针对利基市场的SaaS”&#xff0c;特点是一般由个人或者小团…

网络爬虫的危害与防御方法

爬虫程序是一种计算机程序&#xff0c;旨在通过执行自动化或重复性任务来模仿或替代人类的操作。爬虫程序执行任务的速度和准确性比真实用户高得多。爬虫程序在互联网上扮演着各种各样的角色&#xff0c;超过一半的网络流量是由爬虫程序产生的。有些爬虫程序非常有用&#xff0…

v-if和v-show的区别?使用场景?v-if状态改变调用钩子函数的示例

文章目录1、v-show与v-if的共同点2、v-show与v-if的区别3、v-show与v-if的使用场景4、附属到组件和普通元素时的情况4.1、v-show4.2、v-if5、具体实现的效果5.1 查看是否渲染5.2 查看调用的钩子函数6、钩子函数实现的过程分析1、v-show与v-if的共同点 v-show和v-if的作用效果是…

共享模型之管程(五)

1.多线程设计模式 1.1.同步模式之保护性暂停 1.1.1.定义 1>.即Guarded Suspension,用在一个线程等待另一个线程的执行结果的场景中; 2>.使用场景 ①.有一个结果(数据)需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject; ②.如果有结果(数据)不断从一个…

Vitepress(一):基础教程

什么是Vitepress Vitepress是使用Vue3Vite来快速搭建一个个人网站的工具&#xff0c;网站搭建者不需要掌握Vue3&#xff0c;Vite等的具体内容&#xff0c;只需要简单的配置就可以生成Vue风格的个人网站 官方地址&#xff1a;https://vitejs.cn/vitepress/ 本教程希望教会大家…

SD Nand 与 SD卡 SDIO模式应用流程

SD Nand/SD卡 SDIO模式应用流程 文章目录SD Nand/SD卡 SDIO模式应用流程1. 前言1.1 参考文档1.2 概述2. Response响应类型及格式3. 各步骤流程3.1 卡识别流程3.2 通讯速率及总线宽度修改流程3.3 擦除流程3.4 单块读流程3.5 单块写流程3.6 多块读流程3.7 多块写流程4. 结束语SD …

Java初识泛型 | 如何通过泛型类/泛型方法实现求数组元素最大值?

目录 一、引言 二、编程分析 1、泛型类实现 思路 代码 2、泛型方法实现 思路 代码 三、拓展&#xff1a;数组排序&#xff08;以冒泡排序为例&#xff09; 1、int类型 原代码 2、泛型类 3、泛型方法 一、引言 给定一个整型数组&#xff0c;求数组中所有元素的最大…

JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)

文章目录前言一、class文件初始化过程1、概述2、初始化过程-案例1a、代码T001_ClassLoadingProcedure 类加载过程b、解析3、初始化过程-案例2a、代码b、解析二、单例模式-双重检查三、硬件层数据一致性1、硬件层的并发优化基础知识b、Intel 的缓存一致性协议&#xff1a;MESI四…

Vivado综合设置之-keep_equivalent_registers

-keep_equivalent_registers即保留等效寄存器&#xff0c;所谓等效寄存器是指共享输入端口&#xff08;输入时钟端口clk和输入数据端口rst&#xff09;的寄存器。 勾选它时&#xff0c;意味着Vivado不会对等效寄存器进行优化&#xff1b; 不勾选它时&#xff08;默认情况&…

eclipse安装UML插件

安装AmaterasUML AmaterasUML 是一个用于 Eclipse 的轻量级 UML 和 ER 图编辑器。 将AmaterasUML的3个jar包拷到Eclpise的plugins文件下&#xff1a; 重启eclipse 在新建菜单中可以发现已经出现了UML文件选项 安装GEF插件&#xff08;Eclipse2018-12 以后无需安装&#xf…

②电子产品拆解分析-电动牙刷

②电子产品拆解分析-电动牙刷一、功能介绍二、电路分析以及器件作用1、振动电机开关控制电路2、锂电池供电与充电电路三、本产品的优缺点1、优点&#xff1a;2、缺点&#xff1a;一、功能介绍 ①5档工作模式&#xff1b;②2分钟倒计时停止工作&#xff1b;③工作续航一个星期以…