【Linux】死锁、读写锁、自旋锁
文章目录
- 1. 死锁
- 1.1 概念
- 1.2 死锁形成的四个必要条件
- 1.3 避免死锁
- 2. 读者写者问题与读写锁
- 2.1 读者写者问题
- 2.2 读写锁的使用
- 2.3 读写策略
- 3. 自旋锁
- 3.1 概念
- 3.2 原理
- 3.3 自旋锁的使用
- 3.4 优点与缺点
1. 死锁
1.1 概念
死锁是指在⼀组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用且不会释放的资源
而处于的⼀种永久等待状态。
所以,可能会造成死锁的局面。
1.2 死锁形成的四个必要条件
- 互斥条件:⼀个资源每次只能被⼀个执行流使用
- 请求与保持条件:⼀个执行流因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:⼀个执行流已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干执行流之间形成⼀种头尾相接的循环等待资源的关系
1.3 避免死锁
方式:破坏死锁的四个必要条件。
互斥条件:
- 破坏难度:由于互斥性是资源访问的基本特性,因此很难或不应该被破坏。
请求和保持条件:
- 破坏策略:可以采用静态分配策略,即进程在运行前一次性申请完它所需要的全部资源,在资源未满足前,它不启动。这样就不会出现占有资源又等待其他资源的情况,从而破坏请求和保持条件。
不剥夺条件
- 破坏策略:可以采用剥夺式调度策略,即当一个进程申请新资源而得不到满足时,可以释放它所占有的部分资源,以便其他进程使用,从而破坏不剥夺条件。
循环等待条件
- 破坏策略:可以采用顺序资源分配法,即首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,只能申请编号比之前大的资源。这样可以避免形成循环等待链,从而破坏循环等待条件。
例如:破环请求与保持条件,使资源⼀次性分配。
lock函数可确保所传递的锁对象全部获取成功,本质就是先申请一把锁,在申请的锁种再申请提供的锁对象,因为申请一把锁的操作是原子的。
#include <iostream>
#include <mutex>
#include <unistd.h>
// 定义两个共享资源(整数变量)和两个互斥锁
int shared_resource1 = 0;
int shared_resource2 = 0;
std::mutex mtx1, mtx2;
// ⼀个函数,同时访问两个共享资源
void access_shared_resources()
{std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);// // 使⽤ std::lock 同时锁定两个互斥锁std::lock(lock1, lock2);// 现在两个互斥锁都已锁定,可以安全地访问共享资源int cnt = 10000;while (cnt){++shared_resource1;++shared_resource2;cnt--;} // 当离开 access_shared_resources 的作⽤域时,lock1 和 lock2 的析构函数会被⾃动调⽤// 这会导致它们各⾃的互斥量被⾃动解锁
}
2. 读者写者问题与读写锁
2.1 读者写者问题
读者写者问题其实与生产者消费者问题类似,都是多线程之间互相同步的一种策略。
例如我们在写博客的时候,在我写的时候你是看不到的,直到我发布出去,你才能看到;与此同时在你看的时候,可能还有很多人都在看。
读者写者问题也应该遵循“321"
原则:3种关系,2种角色,1个交易场所。
三种关系如下:
- 写者与写者之间互斥,即一个写者在修改数据时,其他写者不能访问。
- 读者与写者之间互斥&&同步,即不能同时有一个线程在读,而另一个线程在写。
- 当一个读者申请进行读操作时,如果已有写者在访问共享资源,则该读者必须等到没有写者访问后才能开始读操作。
- 当一个写者申请进行写操作时,如果已有读者正在读取数据,写者必须等待所有读者完成读取后才能开始写操作。
- 读者之间可以
并发
(即没关系),即可以有一个或多个读者在读。
读者写者 vs 生成者消费者
二者最大的区别就是:消费者会“取走”共享资源,而读者不会。
伪代码理解读者写者的逻辑:
2.2 读写锁的使用
初始化:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
销毁:
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁:
- 读者加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
- 写者加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
解锁:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
#include <iostream>
#include <unistd.h>#include <pthread.h>
#include <sys/types.h>pthread_rwlock_t rwlock; // 读写锁
static int g_data = 0; // 共享资源pthread_mutex_t lockScreen;void *Reader(void *args)
{int id = *(int *)args;while (true){pthread_rwlock_rdlock(&rwlock); // 读者加锁pthread_mutex_lock(&lockScreen);std::cout << "读者-" << id << " 正在读取数据, 数据是: " << g_data << std::endl;pthread_mutex_unlock(&lockScreen);sleep(2);pthread_rwlock_unlock(&rwlock); // 读者解锁sleep(1);}delete (int *)args;return nullptr;
}void *Writer(void *args)
{int id = *(int *)args;while (true){pthread_rwlock_wrlock(&rwlock); // 写者加锁g_data += 2; // 修改共享数据pthread_mutex_lock(&lockScreen);std::cout << "写者- " << id << " 正在写入. 新的数据是: " << g_data << std::endl;pthread_mutex_unlock(&lockScreen);sleep(1);pthread_rwlock_unlock(&rwlock); // 解锁sleep(1);}delete (int *)args;return NULL;
}int main()
{pthread_rwlock_init(&rwlock, nullptr); // 初始化读写锁pthread_mutex_init(&lockScreen,nullptr);const int reader_num = 5; // 读者数量const int writer_num = 10; // 写者数量const int total = reader_num + writer_num;pthread_t threads[total];for (int i = 0; i < reader_num; i++){int *id = new int(i);pthread_create(&threads[i], 0, Reader, id);}for (int i = reader_num; i < total; i++){int *id = new int(i - reader_num);pthread_create(&threads[i], 0, Writer, id);}for (int i = 0; i < total; i++){pthread_join(threads[i], nullptr);}return 0;
}
2.3 读写策略
- 读者优先
在这种策略中, 系统会尽可能多地允许多个读者同时访问资源(比如共享文件或数据) , 而不会优先考虑写者。 这意味着当有读者正在读取时, 新到达的读者会立即被允许进入读取区, 而写者则会被阻塞, 直到所有读者都离开读取区。 读者优先策略可能会导致写者饥饿(即写者长时间无法获得写入权限), 特别是当读者频繁到达时。 - 写者优先
在这种策略中, 系统会优先考虑写者。 当写者请求写入权限时, 系统会尽快地让写者进入写入区, 即使此时有读者正在读取。 这通常意味着一旦有写者到达, 所有后续的读者都会被阻塞, 直到写者完成写入并离开写入区。 写者优先策略可以减少写者等待的时间, 但可能会导致读者饥饿(即读者长时间无法获得读取权限) , 特别是当写者频繁到达时。
选择合适的策略时,需要根据具体的应用场景和需求进行权衡。例如,在需要频繁读取而写入较少的应用中,读者优先策略可能更为合适;而在需要频繁写入的应用中,写者优先策略可能更为合适。
3. 自旋锁
3.1 概念
在我们之前讲的信号量或互斥锁,都有一个特点:申请锁失败,申请线程都要阻塞挂起等待。
但是,当一个线程在临界区内执行的时长非常短时,那么等待线程阻塞、挂起、唤醒的代价是比较大的。所以有一种锁在申请临界区的时候,可以不阻塞等待,它会持续自旋(即在一个循环中不断检查锁是否可用),这种状态的锁我们称之为自旋锁
。
这种机制减少了线程切换的开销, 适用于短时间内锁的竞争情况。
3.2 原理
自旋锁通常使用一个共享的标志位(如一个布尔值)来表示锁的状态。 当标志位为true 时,表示锁已被某个线程占用;当标志位为 false 时,表示锁可用。 当一个线程尝试获取自旋锁时,它会不断检查标志位:
- 如果标志位为 false,表示锁可用, 线程将设置标志位为 true, 表示自己占用了锁, 并进入临界区。
- 如果标志位为 true(即锁已被其他线程占用),线程会在一个循环中不断自旋等待, 直到锁被释放
上面检测标志位的操作一定是原子性的。
伪代码理解原理:
3.3 自旋锁的使用
Linux 提供的自旋锁系统调用
- 初始化:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
-
销毁:
int pthread_spin_destroy(pthread_spinlock_t *lock);
-
加锁
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
申请锁失败就返回,就可以使用适当的退避策略了。 -
解锁:
int pthread_spin_unlock(pthread_spinlock_t *lock);
使用:
#include <iostream>
#include <unistd.h>
#include <pthread.h>pthread_spinlock_t spinlock; //自旋锁static int g_ticket = 5000;void* func(void* args)
{char* name = (char*)args;while(true){pthread_spin_lock(&spinlock);if(g_ticket > 0){usleep(1000);printf("%s sells ticket:%d\n", name, g_ticket);g_ticket--;pthread_spin_unlock(&spinlock);}else{pthread_spin_unlock(&spinlock);break;}}return nullptr;
}int main()
{pthread_spin_init(&spinlock,PTHREAD_PROCESS_PRIVATE); //初始化pthread_t t1,t2,t3,t4,t5;pthread_create(&t1,nullptr,func,(void*)"thread-1");pthread_create(&t2,nullptr,func,(void*)"thread-2");pthread_create(&t3,nullptr,func,(void*)"thread-3");pthread_create(&t4,nullptr,func,(void*)"thread-4");pthread_create(&t5,nullptr,func,(void*)"thread-5");pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);pthread_join(t4,nullptr);pthread_join(t5,nullptr);pthread_spin_destroy(&spinlock); //销毁return 0;
}
3.4 优点与缺点
优点
- 低延迟: 自旋锁适用于短时间内的锁竞争情况, 因为它不会让线程进入休眠状态, 从而避免了线程切换的开销, 提高了锁操作的效率。
- 减少系统调度开销: 等待锁的线程不会被阻塞, 不需要上下文切换, 从而减少了系统调度的开销。
缺点
- CPU 资源浪费: 如果锁的持有时间较长,等待获取锁的线程会一直循环等待,导致 CPU 资源的浪费。
- 可能引起活锁: 当多个线程同时自旋等待同一个锁时, 如果没有适当的退避策略, 可能会导致所有线程都在不断检查锁状态而无法进入临界区, 形成活锁。
使用场景
- 短暂等待的情况: 适用于锁被占用时间很短的场景, 如多线程对共享数据进行简单的读写操作。
- 多线程锁使用: 通常用于系统底层, 同步多个 CPU 对共享资源的访问。
相关文章:
【Linux】死锁、读写锁、自旋锁
文章目录 1. 死锁1.1 概念1.2 死锁形成的四个必要条件1.3 避免死锁 2. 读者写者问题与读写锁2.1 读者写者问题2.2 读写锁的使用2.3 读写策略 3. 自旋锁3.1 概念3.2 原理3.3 自旋锁的使用3.4 优点与缺点 1. 死锁 1.1 概念 死锁是指在⼀组进程中的各个进程均占有不会释放的资源…...
Spring Web开发(请求)获取JOSN对象| 获取数据(Header)
大家好,我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…...
用c语言完成俄罗斯方块小游戏
用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发,怎么说呢,在这里只针对刚学程序设计的学生,就是说刚接触C语言没多久,有一点功底的学生看看,简陋的代码,简陋的实现࿰…...
SpringBoot整合Retry详细教程
问题背景 在现代的分布式系统中,服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略,它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制,这可以通过…...
JS API事件监听(绑定)
事件监听 语法 元素对象.addEventListener(事件监听,要执行的函数) 事件监听三要素 事件源:那个dom元素被事件触发了,要获取dom元素 事件类型:用说明方式触发,比如鼠标单击click、鼠标经过mouseover等 事件调用的函数&#x…...
ceph手动部署
ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本: Rocky Linux release …...
superset load_examples加载失败解决方法
如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …...
wareshark分析mysql协议的数据包
使用wareshark 分析mysql协议的数据包,是每个dba都应该掌握的技能,掌握以后,就可以通过tcpdump抓包分析,得到连接报错的信息了。 tcpdump抓包命令: tcpdump -nn -i bond0 dst 10.21.6.72 and port 4002 -w 1129_tcpdu…...
HarmonyOS4+NEXT星河版入门与项目实战(25)------UIAbility启动模式(文档编辑案例)
文章目录 1、启动模式2、Specified启动模式实现步骤3、文档编辑案例1、文件创建2代码实现3、Statge 创建4、添加配置1、启动模式 Singleton启动模式: 每个 UIAbility 只存在一个实例,是默认的启动模式,任务列表中只会存在一个相同的 UIAbilityStandard启动模式: 每次启动 U…...
webpack 项目访问静态资源
使用 webpack dev serve 启动 react 项目后,发现无法使用 http://localhost:8080/1.png 访问到项目的 /static 目录下的 1.png 文件。我的 webpack-dev.js 配置如下: const webpack require(webpack) const webpackMerge require(webpack-merge) cons…...
UNION和UNION ALL区别
文章目录 结果集的处理方式:对重复记录的处理:排序处理:执行效率: UNION和UNION ALL的主要区别在于结果集的处理方式、对重复记录的处理、排序处理以及执行效率。 结果集的处理方式: UNION…...
Rook入门:打造云原生Ceph存储的全面学习路径(下)
文章目录 六.Rook部署云原生CephFS文件系统6.1 部署cephfs storageclass6.2 创建容器所需cephfs文件系统6.3创建容器pod使用rook-cephfs提供pvc6.4 查看pod是否使用rook-cephfs 七.Ceph Dashboard界面7.1 启用dashboard开关7.2 ceph-dashboard配置外部访问7.3 Dashboard web ad…...
RabbitMQ消息可靠性保证机制6--可靠性分析
在使用消息中间件的过程中,难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程(轨迹溯源),帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪,Firehose可…...
k8s容器存储接口 CSI 相关知识
容器存储接口 CSI 相关知识 参考: https://blog.csdn.net/lovely_nn/article/details/122880876 https://developer.aliyun.com/article/783464 https://www.cnblogs.com/varden/p/15139819.html存储商需实现 CSI 插件的 NodeGetVolumeStats 接口,Kube…...
jmeter基础_打开1个jmeter脚本(.jmx文件)
课程大纲 方法1.菜单栏“打开” 菜单栏“文件” - “打开” (或快捷键,mac为“⌘ O”),打开文件选择窗口 - 选择脚本文件,点击“open”,即可打开脚本。 方法2.工具栏“打开”图标 工具栏点击“打开”图标&…...
Linux---对时/定时服务
文章目录 目录 文章目录 前言 一.对时服务 服务端配置 客户端配置 二.定时服务 单次定时任务 循环定时任务 前言 在当今信息化高速发展的时代,时间的准确性和任务的定时执行对于各种系统和服务来说至关重要。Linux操作系统,凭借其强大的功能和灵活的…...
Agent
Agent核心 1、自主性 2、交互性 3、适应性 4、目的性 ReAct Reasoning and Acting范式 模型的推理过程分为 推理 Reason 和行动 Action 两个步骤,交替执行,直至获得最终结果。 推理 Reason 生成分析步骤,解释当前任务的上下文或状态…...
Oracle 数据库执行增删改查命令的原理与过程
摘要: 本文深入探讨当向 Oracle 数据库发送一个增删改查(CRUD)命令时,数据库内部的执行机制与详细过程。从用户发起命令开始,逐步剖析命令在 Oracle 数据库体系结构各组件中的流转、解析、优化以及执行路径,…...
HBase难点
查询优化 一次Scan会返回大量数据,客户端向HBase发送一次Scan请求,实际上并不会将所有数据加载到本地,而是通过多次RPC请求进行加载,防止客户端OOM。禁止缓存优化:批量读取数据时会全表扫描一次业务表,这种…...
Y20030023 PHP+thinkphp+MYSQL+LW+基于PHP的健身房管理系统的设计与实现 源代码 配置 初稿
基于PHP的健身房管理系统 1.项目摘要2. 系统开发的背景及意义3.项目功能4.界面展示5.源码获取 1.项目摘要 近年来,随着社会发展和科技进步,人们越来越重视健康养生并关注电子商务对日常交流方式的影响。随着健身行业消费人群的增加,竞争变得…...
mongodb下载与使用
下载地址: Install MongoDB Community Kubernetes Operator | MongoDB 1、安装MongoDB (5.0.30) 将压缩包移动到C:\Program Files 下,然后解压创建C:\data\db目录,mongodb 会将数据默认保存在这个文件夹以mongodb 中 bin目录作为工作目录&…...
【Linux基础】Linux基本指令
目录 1、pwd 指令 2、clear 指令 3、ls 指令 1、ls 显示当前路径下的文件或者目录名称 2、ls -l 显示当前路径下的文件或者目录的更详细的属性信息 3、ls -a :显示所有文件,包括隐藏文件 4、ls -d 将目录像文件一样显示,而不是显示其…...
【RISC-V CPU debug 专栏 3 -- Debugging RISC-V Cores】
文章目录 RISC-V 调试规范开源与多样性挑战调试规范的重要性外部调试支持的主要组件调试功能Lauterbach 的贡献RISC-V 调试规范 调试 RISC-V 内核涉及许多独特的挑战,这是由 RISC-V 的开源特性和多样化的生态系统所决定的。为了避免专有调试接口的泛滥,RISC-V 基金会内的工作…...
思科实现网络地址转换(NAT)和访问控制列表(ACL)和动态路由配置并且区分静态路由和动态路由配置。
实验拓扑(分为静态路由和动态路由两种) 静态路由互通 动态路由互通 实验背景 这个是想实现外网与内网的连接跟网络的探讨,最终实现互通以及使用并且在网络地址转换后能使用网络然后再这个基础上再配置访问控制列表和网络地址转换的的学习过程。 实验需了解的知识…...
基于 Python、OpenCV 和 PyQt5 的人脸识别上课打卡系统
大家好,我是Java徐师兄,今天为大家带来的是基于 Python、OpenCV 和 PyQt5 的人脸识别上课签到系统。该系统采用 Python 语言开发,开发过程中采用了OpenCV框架,Sqlite db 作为数据库,系统功能完善 ,实用性强…...
MySQL 复合查询
实际开发中往往数据来自不同的表,所以需要多表查询。本节我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE 来演示如何进行多表查询。表结构的代码以及插入的数据如下: DROP database IF EXISTS scott; CREATE database IF NOT EXIST…...
【Leetcode】19. 删除链表的第N个节点
【Leetcode】19. 删除链表的第N个节点 1. 题目介绍2. 方法一:计算链表长度逻辑流程:代码复杂度分析 1. 题目介绍 题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head [1,2,…...
flutter底部导航栏中间按钮凸起,导航栏中间部分凹陷效果
关键代码: Scaffold中设置floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked和extendBody: true, BottomAppBar中设置shape: const CircularNotchedRectangle() Scaffold(extendBody: true,//body是否延伸脚手架底部,在底部导航…...
<项目代码>YOLOv8 红绿灯识别<目标检测>
YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…...
HTMLHTML5革命:构建现代网页的终极指南 - 4. 使用元素
HTML&HTML5革命:构建现代网页的终极指南 4. 使用元素 大家好,我是莫离老师。 到目前为止,我们已经了解了 HTML 和 HTML5 的基础概念,并且选择了适合自己的开发工具。 今天,我们将迈出实际开发的第一步,…...
做网站制作较好的公司/成都疫情最新消息
一.简介 0. 页面的生命周期。 1. WebForm后台页面类继承于Page类,Page类实现了IHttpHandler接口。 2. 前台页面类继承于后台页面类。 3. 先调用PageLoad方法,再调用Render方法生成html代码。 二. 加密安全 互联网没有绝对的安全,登…...
深圳大眼睛网站建设/b2b电子商务网站
SpringCloud(第 018 篇)Zuul 服务 API 网关微服务之代理与反向代理 - 一、大致介绍 1、API 服务网关顾名思义就是统一入口,类似 nginx、F5 等功能一样,统一代理控制请求入口,弱化各个微服务被客户端记忆功能࿱…...
政府单位网站建设改版方案/西安 做网站
golang 模板(template)的常用基本语法 模板 在写动态页面的网站的时候,我们常常将不变的部分提出成为模板,可变部分通过后端程序的渲染来生成动态网页,golang提供了html/template包来支持模板渲染。 这篇文章不讨论golang后端的模板读取及渲染…...
企业网站建设预算方案/网站访问量
iOS8之后,苹果推出了WebKit这个框架,用来替换原有的UIWebView,新的控件优点多多,不一一叙述。由于一直在适配iOS7,就没有去替换,现在仍掉了iOS7,以为很简单的就替换过来了,然而在替换…...
泰州市建设监理协会网站/网络营销活动案例
作为一只linux菜的不能更菜的鸟,在linux前台运行了一个服务后,我必须退出后才能执行其他命令,可是我的服务需要一直运行,于是我同事就给我在该命令后加了">/dev/null 2>&1"据说是这条命令就可以转向后台了&am…...
好用的网站后台/域名申请的流程
题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: …...