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

【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. 互斥条件:⼀个资源每次只能被⼀个执行流使用
  2. 请求与保持条件:⼀个执行流因请求资源而阻塞时,对已获得的资源保持不放在这里插入图片描述
  3. 不剥夺条件:⼀个执行流已获得的资源,在未使用完之前,不能强行剥夺
    在这里插入图片描述
  4. 循环等待条件:若干执行流之间形成⼀种头尾相接的循环等待资源的关系
    在这里插入图片描述

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)

大家好&#xff0c;我叫小帅今天我们来继续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语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…...

SpringBoot整合Retry详细教程

问题背景 在现代的分布式系统中&#xff0c;服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略&#xff0c;它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制&#xff0c;这可以通过…...

JS API事件监听(绑定)

事件监听 语法 元素对象.addEventListener(事件监听,要执行的函数) 事件监听三要素 事件源&#xff1a;那个dom元素被事件触发了&#xff0c;要获取dom元素 事件类型&#xff1a;用说明方式触发&#xff0c;比如鼠标单击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 操作系统版本&#xff1a; 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协议的数据包&#xff0c;是每个dba都应该掌握的技能&#xff0c;掌握以后&#xff0c;就可以通过tcpdump抓包分析&#xff0c;得到连接报错的信息了。 tcpdump抓包命令&#xff1a; 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 项目后&#xff0c;发现无法使用 http://localhost:8080/1.png 访问到项目的 /static 目录下的 1.png 文件。我的 webpack-dev.js 配置如下&#xff1a; const webpack require(webpack) const webpackMerge require(webpack-merge) cons…...

‌UNION和UNION ALL区别

文章目录 结果集的处理方式‌&#xff1a;‌对重复记录的处理‌&#xff1a;‌排序处理‌&#xff1a;‌执行效率‌&#xff1a; ‌UNION和UNION ALL的主要区别在于结果集的处理方式、对重复记录的处理、排序处理以及执行效率。‌‌ 结果集的处理方式‌&#xff1a; ‌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--可靠性分析

在使用消息中间件的过程中&#xff0c;难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程&#xff08;轨迹溯源&#xff09;&#xff0c;帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪&#xff0c;Firehose可…...

k8s容器存储接口 CSI 相关知识

容器存储接口 CSI 相关知识 参考&#xff1a; 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 接口&#xff0c;Kube…...

jmeter基础_打开1个jmeter脚本(.jmx文件)

课程大纲 方法1.菜单栏“打开” 菜单栏“文件” - “打开” &#xff08;或快捷键&#xff0c;mac为“⌘ O”&#xff09;&#xff0c;打开文件选择窗口 - 选择脚本文件&#xff0c;点击“open”&#xff0c;即可打开脚本。 方法2.工具栏“打开”图标 工具栏点击“打开”图标&…...

Linux---对时/定时服务

文章目录 目录 文章目录 前言 一.对时服务 服务端配置 客户端配置 二.定时服务 单次定时任务 循环定时任务 前言 在当今信息化高速发展的时代&#xff0c;时间的准确性和任务的定时执行对于各种系统和服务来说至关重要。Linux操作系统&#xff0c;凭借其强大的功能和灵活的…...

Agent

Agent核心 1、自主性 2、交互性 3、适应性 4、目的性 ReAct Reasoning and Acting范式 模型的推理过程分为 推理 Reason 和行动 Action 两个步骤&#xff0c;交替执行&#xff0c;直至获得最终结果。 推理 Reason 生成分析步骤&#xff0c;解释当前任务的上下文或状态…...

Oracle 数据库执行增删改查命令的原理与过程

摘要&#xff1a; 本文深入探讨当向 Oracle 数据库发送一个增删改查&#xff08;CRUD&#xff09;命令时&#xff0c;数据库内部的执行机制与详细过程。从用户发起命令开始&#xff0c;逐步剖析命令在 Oracle 数据库体系结构各组件中的流转、解析、优化以及执行路径&#xff0c…...

HBase难点

查询优化 一次Scan会返回大量数据&#xff0c;客户端向HBase发送一次Scan请求&#xff0c;实际上并不会将所有数据加载到本地&#xff0c;而是通过多次RPC请求进行加载&#xff0c;防止客户端OOM。禁止缓存优化&#xff1a;批量读取数据时会全表扫描一次业务表&#xff0c;这种…...

Y20030023 PHP+thinkphp+MYSQL+LW+基于PHP的健身房管理系统的设计与实现 源代码 配置 初稿

基于PHP的健身房管理系统 1.项目摘要2. 系统开发的背景及意义3.项目功能4.界面展示5.源码获取 1.项目摘要 近年来&#xff0c;随着社会发展和科技进步&#xff0c;人们越来越重视健康养生并关注电子商务对日常交流方式的影响。随着健身行业消费人群的增加&#xff0c;竞争变得…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...