Linux中读写锁详细介绍
读写锁介绍
Linux 中的读写锁(Read-Write Lock)是一种用于线程同步的机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种机制在读操作远多于写操作的场景下,可以显著提高并发性能。读写锁主要有以下特点:
读写锁区分了对共享资源的读取和写入操作。这使得它可以根据不同的操作类型,采用不同的锁定策略。
对于读锁(共享)来说,允许多个线程同时持有,用于保护读取操作,当有线程持有读锁时,其他线程也可以获取读锁。
对于写锁(独占)来说,一次只能有一个线程持有,用于保护写入操作,当有线程持有写锁时,其他线程都不能获取读锁或者写锁。
想要获取读锁和写锁的要求:
一个线程想要成功获取某资源的读锁,要求其他线程不持有任何锁或者只持有读锁可以获取成功,如果其他线程持有该资源写锁的话就获取不成功。
一个线程想要成功获取某资源的写锁,要求其他线程不持有任何锁才可以获取成功,如果其他线程持有该资源写锁或者该资源读锁的话就获取不成功。
读写锁的适用情况
读写锁的优点:
- 提高读操作并发性:在读多写少的场景下,读写锁可以显著提高读取操作的并发性,从而提高程序的整体性能。
- 减少线程阻塞:与互斥锁相比,读写锁减少了线程因读取操作而阻塞的概率,这提高了线程的利用率和程序的响应速度。
读写锁的适用情况:
- 读多写少的共享资源:
- 例如,配置文件、数据缓存、查找表等。
- 需要频繁读取但很少修改的数据:
- 例如,传感器数据、实时监控数据等。
互斥锁VS读写锁互斥锁确保了在任何时刻只有一个线程可以访问受保护的资源,无论是读取还是写入。
读写锁的适合使用在在读操作远多于写操作的场景下,它允许多个线程同时读取共享资源,提高了读操作的并发性,但只允许一个线程写入,对于资源写操作没有提高。
读写锁的使用流程
- 初始化读写锁: 使用
pthread_rwlock_init()创建读写锁。 - 获取锁:
读取共享资源前,使用pthread_rwlock_rdlock()获取读锁。
写入共享资源前,使用pthread_rwlock_wrlock()获取写锁。
- 访问共享资源: 在读写锁的保护下,安全地访问共享资源。
- 解锁读写锁: 在完成共享资源访问后,使用
pthread_rwlock_unlock()解锁读写锁。 - 销毁读写锁: 在读写锁不再使用时,使用
pthread_rwlock_destroy()销毁它。
pthread_rwlock_init():初始化读写锁
函数作用:初始化一个读写锁。
函数原型:int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
参数:
- rwlock:指向要初始化的读写锁变量的指针。
- attr:读写锁属性,通常设置为NULL表示使用默认属性。
返回值:
成功返回0,失败返回错误码。
注意:
- 为了让多个线程都能使用同一个读写锁,读写锁变量必须定义于线程可以共享访问的内存区域,比如全局变量静态变量或者堆上,读写锁变量的类型为pthread_rwlock_t。
- 使用读写锁的第一步必须是初始化读写锁,不初始化可能会出现未定义情况。
- 通常来说pthread_rwlock_init函数的第二个参数attr我们会设置为NULL,此时会创建一个默认属性的读写锁,完全已经够用。
pthread_rwlock_rdlock():获取读锁
函数作用:获取读锁。
函数原型:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数:
rwlock:指向要获取读锁的读写锁变量的指针。
返回值:
成功返回0,失败返回错误码。
注意:
- 如果当前没有线程持有写锁,调用线程将立即获得读锁。
- 如果当前有线程持有写锁,调用线程将被阻塞,直到写锁被释放。
pthread_rwlock_tryrdlock():尝试获取读锁
函数作用:尝试获取读锁,如果读写锁已被写锁定,则不阻塞,立即返回错误。
函数原型:int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
参数:
rwlock:指向要尝试获取读锁的读写锁变量的指针。
返回值:
- 成功锁定返回0。
- 如果读写锁已被写锁定,返回
EBUSY。 - 其他错误返回相应的错误码。
注意:
- pthread_rwlock_tryrdlock()是非阻塞的,它不会使调用线程进入等待状态。
- 这个函数在需要非阻塞的锁定操作时很有用。
pthread_rwlock_wrlock():获取写锁
函数作用:获取写锁。
函数原型:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数:
rwlock:指向要获取写锁的读写锁变量的指针。
返回值:
成功返回0,失败返回错误码。
注意:
- 如果当前没有线程持有读锁或写锁,调用线程将立即获得写锁。
- 如果当前有线程持有读锁或写锁,调用线程将被阻塞,直到所有锁都被释放。
pthread_rwlock_trywrlock():尝试获取写锁
函数作用:尝试获取写锁,如果读写锁已被读锁定或写锁定,则不阻塞,立即返回错误。
函数原型:int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
参数:
rwlock:指向要尝试获取写锁的读写锁变量的指针。
返回值:
- 成功锁定返回0。
- 如果读写锁已被读锁定或写锁定,返回
EBUSY。 - 其他错误返回相应的错误码。
注意:
- pthread_rwlock_trywrlock()是非阻塞的,它不会使调用线程进入等待状态。
- 这个函数在需要非阻塞的锁定操作时很有用。
pthread_rwlock_unlock():解锁读写锁
函数作用:解锁读写锁
函数原型:int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
参数:
rwlock:指向要解锁的读写锁变量的指针。
返回值:
成功返回0,失败返回错误码。
注意:
- 解锁读写锁后,其他等待该读写锁的线程可以获得锁。
- 持有读锁或写锁的线程都可以使用这个函数解锁。
一个线程对于同一个读写锁,在同一时间点上,只能拥有读锁或者写锁,不能同时拥有。
所以使用pthread_rwlock_unlock()进行解锁时,不需要指定解的是读锁还是写锁。操作系统内部会维护读写锁的状态,并且根据内部状态来确定如何解锁,这样设计简化了读写锁的使用
pthread_rwlock_destroy():销毁读写锁
函数作用:销毁一个读写锁。
函数原型:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
rwlock:指向要销毁的读写锁变量的指针。
返回值:
成功返回0,失败返回错误码。
注意:
- 在读写锁不再使用时,应该及时销毁它以释放资源。
- 只有未被任何线程锁定的读写锁才能被销毁。
读写锁使用示例
示例:共享数据缓存
假设我们有一个共享的数据缓存,多个线程需要读取缓存中的数据,而只有少数线程需要更新缓存。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>#define NUM_READERS 5
#define NUM_WRITERS 2// 共享数据缓存
int data_cache = 0;
// 读写锁
pthread_rwlock_t rwlock;// 读线程函数
void *reader_thread(void *arg) {int thread_id = *(int *)arg;// 获取读锁pthread_rwlock_rdlock(&rwlock);// 读取共享数据printf("Reader %d: Reading data_cache = %d\n", thread_id, data_cache);sleep(1); // 模拟耗时读取操作// 释放读锁pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}// 写线程函数
void *writer_thread(void *arg) {int thread_id = *(int *)arg;// 获取写锁pthread_rwlock_wrlock(&rwlock);// 更新共享数据data_cache++;printf("Writer %d: Updated data_cache = %d\n", thread_id, data_cache);sleep(2); // 模拟耗时写入操作// 释放写锁pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}int main() {pthread_t readers[NUM_READERS];pthread_t writers[NUM_WRITERS];int reader_ids[NUM_READERS];int writer_ids[NUM_WRITERS];// 初始化读写锁if (pthread_rwlock_init(&rwlock, NULL) != 0) {perror("pthread_rwlock_init");exit(EXIT_FAILURE);}// 创建读线程for (int i = 0; i < NUM_READERS; i++) {reader_ids[i] = i;if (pthread_create(&readers[i], NULL, reader_thread, &reader_ids[i]) != 0) {perror("pthread_create (reader)");exit(EXIT_FAILURE);}}// 创建写线程for (int i = 0; i < NUM_WRITERS; i++) {writer_ids[i] = i;if (pthread_create(&writers[i], NULL, writer_thread, &writer_ids[i]) != 0) {perror("pthread_create (writer)");exit(EXIT_FAILURE);}}// 等待读线程结束for (int i = 0; i < NUM_READERS; i++) {pthread_join(readers[i], NULL);}// 等待写线程结束for (int i = 0; i < NUM_WRITERS; i++) {pthread_join(writers[i], NULL);}// 销毁读写锁if (pthread_rwlock_destroy(&rwlock) != 0) {perror("pthread_rwlock_destroy");exit(EXIT_FAILURE);}return 0;
}
代码解释:
- 包含头文件:
stdio.h:用于输入输出。stdlib.h:用于标准库函数。pthread.h:用于线程相关函数。unistd.h:用于sleep()函数。
- 定义共享数据和读写锁:
data_cache:一个整数变量,作为共享数据缓存。rwlock:一个pthread_rwlock_t类型的变量,用于读写锁。
- 读线程函数
reader_thread():- 每个读线程执行这个函数。
- 首先,使用
pthread_rwlock_rdlock()获取读锁。 - 然后,读取共享数据
data_cache。 - 使用
sleep()模拟耗时读取操作。 - 最后,使用
pthread_rwlock_unlock()释放读锁。
- 写线程函数
writer_thread():- 每个写线程执行这个函数。
- 首先,使用
pthread_rwlock_wrlock()获取写锁。 - 然后,更新共享数据
data_cache。 - 使用
sleep()模拟耗时写入操作。 - 最后,使用
pthread_rwlock_unlock()释放写锁。
main()函数:- 初始化读写锁:使用
pthread_rwlock_init()初始化读写锁。 - 创建读线程:创建多个读线程,并传递线程ID作为参数。
- 创建写线程:创建多个写线程,并传递线程ID作为参数。
- 等待线程结束:使用
pthread_join()等待所有线程结束。 - 销毁读写锁:使用
pthread_rwlock_destroy()销毁读写锁。
- 初始化读写锁:使用
程序输出显示读线程并发读取共享数据的过程,以及写线程更新共享数据的过程。由于读写锁的保护,读线程可以并发执行,而写线程的更新操作是互斥的。
相关文章:
Linux中读写锁详细介绍
读写锁介绍 Linux 中的读写锁(Read-Write Lock)是一种用于线程同步的机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种机制在读操作远多于写操作的场景下,可以显著提高并发性能。读写锁主要有…...
flink分布式事务 - 两阶段提交
分布式事务与两阶段提交协议详解 分布式事务是分布式系统中保证数据一致性和可靠性的核心技术之一。在大数据处理、微服务架构以及实时流处理等领域,分布式事务的应用场景越来越广泛。两阶段提交协议(Two-Phase Commit, 2PC)作为一种经典的分布式事务管理协议,在保证强一致…...
《DataWorks:为人工智能算法筑牢高质量数据根基》
在当今数字化时代,人工智能(AI)技术的迅猛发展深刻地改变着各个行业的面貌。从智能推荐系统到医疗影像诊断,从自动驾驶到自然语言处理,AI正以前所未有的速度渗透到我们生活和工作的方方面面。而在这一系列AI应用的背后…...
机器学习(五)
一,多类(Multiclass) 多类是指输出不止有两个输出标签,想要对多个种类进行分类。 Softmax回归算法: Softmax回归算法是Logistic回归在多类问题上的推广,和线性回归一样,将输入的特征与权重进行…...
DeepSeek搭配Excel,制作自定义按钮,实现办公自动化!
今天跟大家分享下我们如何将DeepSeek生成的VBA代码,做成按钮,将其永久保存在我们的Excel表格中,下次遇到类似的问题,直接在Excel中点击按钮,就能10秒搞定,操作也非常的简单. 一、代码准备 代码可以直接询问…...
利用Git和wget批量下载网页数据
一、Git的下载(参考文章) 二. wget下载(网上很多链接) 三、git和wget结合使用 1.先建立一个文本,将代码写入文本(代码如下),将txt后缀改为sh(download_ssebop.sh…...
人工智能之数学基础:线性代数中的行列式的介绍
本文重点 行列式是一种重要的数学工具,更是连接众多数学概念和实际应用的桥梁。本文将介绍矩阵的行列式,你可以把它看成对方阵的一种运算,将方阵映射成一个标量。 行列式的定义 行列式是一个由数值组成的方阵所确定的一个标量值。对于一个n*n的矩阵A=(aij),其行列式记为d…...
[自然语言处理]pytorch概述--什么是张量(Tensor)和基本操作
pytorch概述 PyTorch 是⼀个开源的深度学习框架,由 Facebook 的⼈⼯智能研究团队开发和维护,于2017年在GitHub上开源,在学术界和⼯业界都得到了⼴泛应⽤ pytorch能做什么 GPU加速自动求导常用网络层 pytorch基础 量的概念 标量…...
[杂学笔记]HTTP1.0和HTTP1.1区别、socket系列接口与TCP协议、传输长数据的时候考虑网络问题、慢查询如何优化、C++的垃圾回收机制
目录 1.HTTP1.0和HTTP1.1区别 2.socket系列接口与TCP协议 3.传输长数据的时候考虑网络问题 4.慢查询如何优化 5.C的垃圾回收机制 1.HTTP1.0和HTTP1.1区别 在连接方式上,HTTP1.0默认采用的是短链接的方式,就建立一次通信,也就是说即使在…...
电商主图3秒法则
1. 基础铁律 ▸ 首图点击率曝光量/点击量 ▸ 黄金3秒:触发冲动 > 信息堆砌 2. 必守三原则 ✔ 单点爆破 → 1核心功能 > 10卖点叠加(反例:电子类目点击率↓18%) ✔ 场景植入 → 带场景主图点击率↑34%(数据源:20…...
DeepSeek DeepEP学习(一)low latency dispatch
背景 为了优化延迟,low lantency使用卡间直接收发cast成fp8的数据的方式,而不是使用normal算子的第一步执行机间同号卡网络发送,再通过nvlink进行转发的两阶段方式。进一步地,normal算子的dispatch包含了notify_dispatch传输meta…...
Metal学习笔记十:光照基础
光和阴影是使场景流行的重要要求。通过一些着色器艺术,您可以突出重要的对象、描述天气和一天中的时间并设置场景的气氛。即使您的场景由卡通对象组成,如果您没有正确地照亮它们,场景也会变得平淡无奇。 最简单的光照方法之一是 Phong 反射模…...
Wpf-ReactiveUI-Usercontrol交互
文章目录 1、使用属性绑定UserControl 部分(MyUserControl.xaml.cs)UserControl 视图模型部分(MyUserControlViewModel.cs)主界面部分(MainWindow.xaml)主界面视图模型部分(MainWindowViewModel.cs)2、使用消息传递UserControl 视图模型部分(MyUserControlViewModel.c…...
报告分享 | 哈工大赛尔实验室——大模型时代的具身智能
本报告详细介绍了大模型时代的具身智能,探讨了智能机器人的发展历程、技术挑战和未来发展方向。( 报告全文下载:具身大模型关键技术与应用(哈尔滨工业大学社会计算与信息检索研究中心).pdf!)...
机器学习校招面经二
快手 机器学习算法 一、AUC(Area Under the ROC Curve)怎么计算?AUC接近1可能的原因是什么? 见【搜广推校招面经四】 AUC 是评估分类模型性能的重要指标,用于衡量模型在不同阈值下区分正负样本的能力。它是 ROC 曲线…...
第四十一:Axios 模型的 get ,post请求
Axios 的 get 请求方式 9.双向数据绑定 v-model - 邓瑞编程 Axios 的 post 请求方式:...
全国青少年航天创新大赛各项目对比分析
全国青少年航天创新大赛各项目对比分析 一、比赛场地对比 项目名称场地尺寸场地特点组别差异筑梦天宫虚拟三维场景动态布局,小学组3停泊处,初高中组6停泊处;涉及传送带、机械臂、传感器等虚拟设备。初中/高中组任务复杂度更高,运…...
20250304在Ubuntu20.04的GUI下格式化exFAT格式的TF卡为ext4格式
20250304在Ubuntu20.04的GUI下格式化exFAT格式的TF卡为ext4格式 2025/3/4 16:47 缘起:128GB的TF卡,只能格式化为NTFS/exFAT/ext4。 在飞凌的OK3588-C下,NTFS格式只读。 exFAT需要改内核来支持。 现在只剩下ext4了。 linux R4默认不支持exFAT…...
服务器配置-从0到分析4:ssh免密登入
该部分涉及到公钥、私钥等部分knowledge,本人仅作尝试 若将本地机器 SSH Key 的公钥放到远程主机,就能无需密码直接远程登录远程主机 1,在客户端生成 ssh 公私钥: 也就是我们本地机器,windows电脑 一路回车即可&am…...
React 组件基础介绍
基本概念:一个组件就是用户界面的一部分,可以有自己的逻辑和外观,组件之间可以互相嵌套、复用多次。每个组件就是一个首字母大写的函数,内部存放了组件的逻辑和试图UI,渲染组件只需要把组件 当成 标签 书写。App 可以视…...
环境变量 ─── linux第14课
本内容为总结: 1. 环境变量本质是配置信息, 在系统配置时起效 . 2. 环境变量具有全局性(子进程可以继承父进程的环境信息,不能继承本地变量) 3. 进程具有独立性 ,环境变量可以进程间传递信息(只读信息) 环境变量 环境变量(environment variables)一般是指在操作系统中用来指定操…...
英文生物信息学技术社区Top10推荐:基本情况、评介和网页链接
英文生物信息学技术社区Top10推荐:基本情况、评介和网页链接 李升伟 一、思考与分析 生物信息学涉及生物数据分析和计算工具,所以相关的社区可能包括论坛、问答平台、资源库等等。 首先,我想到Biostars,这是一个比较知名的生物信…...
基于APDL语言的结构优化设计
1、前言 结构设计是创造结构方案的过程,传统的结构设计是设计者按设计要求和设计者的实践经验,参考类似工程,通过判断创造结构方案,然后进行力学分析或按规范要求作安全校核,再修改设计。 而结构优化设计与分析则把力…...
五、Redis 持久化:RDB 与 AOF 深入解析与优化策略
Redis 持久化:RDB 与 AOF 深入解析与优化策略 在 Redis 作为高性能缓存和数据库使用的过程中,数据持久化 是一个关键问题。Redis 提供了两种主要的持久化机制:RDB(Redis Database 快照) 和 AOF(Append-Only File 日志)。本文将详细介绍 Redis 持久化机制的原理、优缺点…...
一、MySQL备份恢复
一、MySQL备份恢复 1.1 MySQL日志管理 数据库中数据丢失或被破坏可能原因 误删除数据库 数据库工作时,意外断电或程序意外终止 由于病毒造成的数据库损坏或丢失 文件系统损坏后,系统进行自检操作 升级数据库时,命令语句不严格 设备故…...
【Linux第三弹】Linux基础指令 (下)
目录 🌟1.find指令 1.1find使用实例 编辑 🌟2.which指令 🌟3.grep指令 3.1grep使用实例 🌟 4.zip/unzip指令 4.1 zip/unzip使用实例 🌟5.tar指令 5.1 tar使用实例 🌟6.完结 很庆幸走在自己…...
VB6网络通信软件开发,上位机开发,TCP网络通信,读写数据并处理,完整源码下载
VB6网络通信软件开发,上位机开发,TCP网络通信,读写数据并处理,完整源码下载 完整源码XZ网口四进四出主动上传版_VB源代码.rar 下载链接:http://xzios.cn:86/WJGL/DownLoadDetial?Id20 在自动化、物联网以及工业控制…...
TMS320F28P550SJ9学习笔记1:CCS导入工程以及测试连接单片机仿真器
学习记录如何用 CCS导入工程以及测试连接单片机仿真器 以下为我的CCS 以及驱动库C2000ware 的版本 CCS版本: Code Composer Studio 12.8.1 C2000ware :C2000Ware_5_04_00_00 目录 CCS导入工程: 创建工程: 添加工程: C…...
阿里万相,正式开源
大家好,我是小悟。 阿里万相正式开源啦。这就像是AI界突然开启了一扇通往宝藏的大门,而且还是免费向所有人敞开的那种。 你想想看,在这个科技飞速发展的时代,AI就像是拥有神奇魔法的魔法师,不断地给我们带来各种意想…...
Glide图片加载优化全攻略:从缓存到性能调优
在 Android 的图片加载库 Glide 中,当图片加载到列表(如 RecyclerView 或 ListView)时,Glide 会根据其内部的线程池和缓存机制来管理图片的加载任务。以下是关于 Glide 在列表中同时异步加载几张图片的相关细节: 1. Gl…...
