【Linux】旋转锁 | 读写锁
在之前的线程学习中,用到的锁都是挂起等待锁,如果申请不到锁,那就会在锁中等待;
自旋锁则不大相似
文章目录
- 1.自旋锁
- 1.1 概念
- 1.2 接口
- 1.2.1 pthread_spin_init/destroy
- 1.2.2 pthread_spin_lock
- 1.2.3 pthread_spin_unlock
- 2.读写锁
- 2.1 读者写者的关系
- 2.2 接口
- 2.2.1 init/destroy
- 2.2.2 读者加锁
- 2.2.3 写者加锁
- 2.2.4 设置锁的属性
- 2.3 代码
1.自旋锁
1.1 概念
自旋锁是一个轮询检测锁,其检测机制并不是挂起等待,而是不断的询问锁有没有空闲;类似于一个while(1)循环的trylock()
由于其需要不断的轮询检测,所以会占用一定的CPU资源;如果线程较多,就容易给cpu造成负荷。
但是自旋锁无须唤醒挂起等待状态的线程,其消耗较小。
总结一下:
- 自旋锁适合竞争不激烈,且临界区较小(呆的时间短)的情况
- 自旋锁不适合大量线程,临界区长的情况
自旋锁的优缺点反过来,便是挂起等待锁的优缺点了。我们要根据不同场景,正确选择锁的类型
1.2 接口
相关接口和mutex都是很相似的,这里就不演示使用的效果了
1.2.1 pthread_spin_init/destroy
#include <pthread.h>int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
1.2.2 pthread_spin_lock
自旋锁同样有trylock接口,用于判断锁是否就绪
#include <pthread.h>
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
1.2.3 pthread_spin_unlock
#include <pthread.h>
int pthread_spin_unlock(pthread_spinlock_t *lock);
2.读写锁
有的时候,我们会有一份config配置文件,这个配置文件会有非常多的线程进行读取,但是很少进行修改和写入。
此时我们如果对配置文件的读取进行加锁,就容易导致效率问题,众多线程不断被阻塞,产生性能损失。
此时,就可以用一个专门的读写锁,对读者和写者加不同的锁,在提升读取性能的同时,保证写入不冲突
2.1 读者写者的关系
写着和写着之间不用多说,肯定是互斥关系;
读者和写者之间也是互斥关系,在写入的时候,不能进行读取,否则容易出现二义性问题;
- 写者写入了一个a,线程甲来读取,得到的结果是a
- 写者继续写入了b,线程乙来读取,得到的结果是ab
这是因为写者的写入还没有完成,导致甲乙读者会获取到完全不同的结果,这是不对的;
读者和读者之间没有关系,因为读者并不会修改数据,也不会取走数据,其存在对临界资源没有影响。
2.2 接口
2.2.1 init/destroy
读写锁只需要初始一个锁就行了,无须对读者写者初始化两个不同的锁
#include <pthread.h>int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
2.2.2 读者加锁
读写锁的读者锁/写者锁是分开的,我们要针对不同的线程调用不同的锁
#include <pthread.h>int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
但是解锁的接口是一样的
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
2.2.3 写者加锁
#include <pthread.h>
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
2.2.4 设置锁的属性
参考 PTHREAD_RWLOCKATTR_SETKIND_NP - Linux手册页
读写锁可以允许我们设置是读者优先还是写者优先。如果采用默认的属性,可能会出现读者一直在读,写者没有办法写入的情况(打印错位是正常情况)
[muxue@bt-7274:~/git/linux/code/23-01-20 rwlock]$ ./test
reader [140484801697536]
reader [140484801697536] 0
reader [140484793304832]
reader [reader [140484784912128140484793304832]
reader [140484784912128] 0
] 0
reader [140484759734016]
reader [140484759734016] 0
reader [140484776519424]
此时就出现了写者饥饿问题,写者无法访问临界资源,饿死了😂
我们可以根据自己的需求进行设置读写锁的属性
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
PTHREAD_RWLOCK_PREFER_READER_NP 一致
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁,避免死锁
*/
下面是一个示例
pthread_rwlock_t rwlock;//锁
pthread_rwlockattr_t attr;//属性
pthread_rwlockattr_init(&attr);//初始化属性
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);//设置锁的属性为写者优先
pthread_rwlock_init(&rwlock, &attr);//初始化并设置读写锁的属性
2.3 代码
#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;volatile int board = 0;//临界资源pthread_rwlock_t rw;//全局读写锁void *reader(void* args)
{const char *name = static_cast<const char *>(args);cout << "reader ["<<pthread_self() <<"]"<< endl;//sleep(1);while(true){pthread_rwlock_rdlock(&rw);cout << "reader ["<<pthread_self() <<"] " << board << endl;pthread_rwlock_unlock(&rw);usleep(110);}
}void *writer(void *args)
{const char *name = static_cast<const char *>(args);//sleep(1);while(true){pthread_rwlock_wrlock(&rw);board++;cout << "writer [" << pthread_self() <<"]"<< endl;pthread_rwlock_unlock(&rw);usleep(100);}
}int main()
{pthread_rwlock_init(&rw, nullptr);pthread_t r1,r2,r3,r4,r5,r6, w1,w2;pthread_create(&r1, nullptr, reader, (void*)"reader");pthread_create(&r2, nullptr, reader, (void*)"reader");pthread_create(&r3, nullptr, reader, (void*)"reader");pthread_create(&r4, nullptr, reader, (void*)"reader");pthread_create(&r5, nullptr, reader, (void*)"reader");pthread_create(&r6, nullptr, reader, (void*)"reader");pthread_create(&w1, nullptr, writer, (void*)"writer");pthread_create(&w2, nullptr, writer, (void*)"writer");pthread_join(r1, nullptr);pthread_join(r2, nullptr);pthread_join(r3, nullptr);pthread_join(r4, nullptr);pthread_join(r5, nullptr);pthread_join(r6, nullptr);pthread_join(w1, nullptr);pthread_join(w2, nullptr);pthread_rwlock_destroy(&rw);return 0;
}
运行结果如下
reader [140067523458816] 7086
reader [140067548636928] 7086
writer [140067515066112]
writer [140067506673408]
reader [140067540244224] 7088
reader [140067565422336] 7088
reader [140067557029632] 7088
reader [140067531851520] 7088
reader [140067523458816] 7088
writer [140067515066112]
writer [140067506673408]
reader [140067548636928] 7090
writer [140067515066112]
reader [140067523458816] 7091
writer [140067506673408]
reader [140067548636928] 7092
reader [140067557029632] 7092
可以看到读者的线程较多,且能够正确读取数据。
如果在读者的whiile中加上sleep(10)
void *reader(void* args)
{const char *name = static_cast<const char *>(args);cout << "reader ["<<pthread_self() <<"]"<< endl;while(true){pthread_rwlock_rdlock(&rw);cout << "reader ["<<pthread_self() <<"] " << board << endl;sleep(10);//睡pthread_rwlock_unlock(&rw);usleep(110);//避免出现只有一个线程工作}
}
能够看到多个读者之间不冲突,不会出现读者A申请锁后,读者B就无法访问临界区的情况。如果是互斥锁,读者A申请之后进入休眠,B就无法申请该锁。

相关文章:
【Linux】旋转锁 | 读写锁
在之前的线程学习中,用到的锁都是挂起等待锁,如果申请不到锁,那就会在锁中等待; 自旋锁则不大相似 文章目录1.自旋锁1.1 概念1.2 接口1.2.1 pthread_spin_init/destroy1.2.2 pthread_spin_lock1.2.3 pthread_spin_unlock2.读写锁…...
EasyExcell导出excel添加水印
EasyExcell导出excel添加水印1、添加easyExcel相关依赖2、准备基础工具类3、创建水印handler类4、创建单元测试类WriteTest.class5、测试结果1、添加easyExcel相关依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId&…...
SpringCloud:Nacos配置管理
Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理…...
正则表达式引擎NFA自动机的回溯解决方案总结
前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具,我们导出了出问题的堆栈信息。 我们可以看到所有的堆栈都指向了一个名为 validateUrl 的方法&#…...
卷积神经网络之AlexNet
目录概述AlexNet特点激活函数sigmoid激活函数ReLu激活函数数据增强层叠池化局部相应归一化DropoutAlexnet网络结构网络结构分析AlexNet各层参数及其数量模型框架形状结构关于数据集训练学习keras代码示例概述 由于受到计算机性能的影响,虽然LeNet在图像分类中取得了…...
React中setState什么时候是同步的,什么时候是异步的?
本文内容均针对于18.x以下版本 setState 到底是同步还是异步?很多人可能都有这种经历,面试的时候面试官给了你一段代码,让你说出输出的内容,比如这样: constructor(props) {super(props);this.state {val: 0}}compo…...
优秀开源软件的类,都是怎么命名的?
日常编码中,代码的命名是个大的学问。能快速的看懂开源软件的代码结构和意图,也是一项必备的能力。 Java项目的代码结构,能够体现它的设计理念。Java采用长命名的方式来规范类的命名,能够自己表达它的主要意图。配合高级的 IDEA&…...
绘制CSP的patterns矩阵图
最近在使用FBCSP处理数据,然后就想着看看处理后的样子,用地形图的形式表现出来,但是没有符合自己需求的函数可以实现,就自己尝试的实现了一下,这里记录一下,方便以后查阅。 绘制CSP的patterns矩阵图 对数据做了FBCSP处理,但是想画一下CSP计算出来的patterns的地形图,并…...
Datatables展示数据(表格合并、日期计算、异步加载数据、分页显示、筛选过滤)
系列文章目录 datatable 自定义筛选按钮的解决方案Echarts实战案例代码(21):front-endPage的CJJTable前端分页插件ajax分页异步加载数据的解决方案 文章目录系列文章目录前言一、html容器构建1.操作按钮2.表格构建二、时间日期计算三、dataTables属性配置1.调用2.过…...
Python decimal模块的使用
Python decimal 模块Python中的浮点数默认精度是15位。Decimal对象可以表示任意精度的浮点数。getcontext函数用于获取当前的context环境,可以设置精度、舍入模式等参数。#在context中设置小数的精度 decimal.getcontext().prec 100通过字符串初始化Decimal类型的变…...
pycharm常用快捷键
编辑类: Ctrl D 复制选定的区域或行 Ctrl Y 删除选定的行 Ctrl Alt L 代码格式化 Ctrl Alt O 优化导入(去掉用不到的包导入) Ctrl 鼠标 简介/进入代码定义 Ctrl / 行注释 、取消注释 Ctrl 左方括号 快速跳到代码开头 Ctrl 右方括…...
useCallback 与 useMemo 的区别 作用
useCallback 缓存钩子函数,useMemo 缓存返回值(计算结果)。 TS声明如下:type DependencyList ReadonlyArray<any>;function useCallback<T extends (...args: any[]) > any>(callback: T, deps: DependencyList)…...
Mybatis的学习
01-mybatis传统dao开发模式 概述 mybatis有两种使用模式: ①传统dao开发模式, ②dao接口代理开发模式 ①传统dao开发模式 dao接口 dao实现子类 mapper映射文件dao实现子类来决定了dao接口的方法和mapper映射文件的statement的关系 代码实现 public class StudentDaoImpl im…...
PyTorch深度学习实战 | 计算机视觉
深度学习领域技术的飞速发展,给人们的生活带来了很大改变。例如,智能语音助手能够与人类无障碍地沟通,甚至在视频通话时可以提供实时翻译;将手机摄像头聚焦在某个物体上,该物体的相关信息就会被迅速地反馈给使用者&…...
力扣(LeetCode)436. 寻找右区间(2023.03.10)
给你一个区间数组 intervals ,其中 intervals[i] [starti, endi] ,且每个 starti 都 不同 。 区间 i 的 右侧区间 可以记作区间 j ,并满足 startj > endi ,且 startj 最小化 。 返回一个由每个区间 i 的 右侧区间 在 interv…...
已解决Servlet中Request请求参数中文乱码的问题
📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言Ὅ…...
【flask】URL和视图映射
目录 首页 传参 URL数据类型 get传参 首页 url与视图函数的映射是通过app.route()装饰器实现的。 只有一个斜杠代表的是根目录——首页。 传参 URL传参是通过<参数名称>的形式进行传递。URL中有几个参数,在视图函数中也要指定几个参数 from flask im…...
Python实现性能测试(locust)
一、安装locustpip install locust -- 安装(在pycharm里面安装或cmd命令行安装都可)locust -V -- 查看版本,显示了就证明安装成功了或者直接在Pycharm中安装locust:搜索locust并点击安装,其他的第三方包也可以通过这种方式二、loc…...
【数论】试除法判断质数,分解质因数,筛质数
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 现已更新完KMP算法、排序模板,之…...
【C++】红黑树
文章目录红黑树的概念红黑树的性质特征红黑树结点的定义红黑树的插入操作情况1情况2情况3特殊情况代码实现红黑树的验证红黑树的删除红黑树和AVL树的比较红黑树的应用红黑树的概念 红黑树,是一种二叉搜索树,但是每一个结点都增加一个存储位表示结点的颜…...
AI教材生成强力工具!低查重保障,让教材编写事半功倍!
梳理教材知识点确实是一项“精细活”,最大的挑战在于平衡和衔接知识之间的关系。如果不小心,很可能会遗漏一些核心知识点,或者在难度的把控上出现问题——小学教材常常写得过于复杂,让学生难以理解;而高中教材又可能显…...
OpenClaw多设备同步:GLM-4.7-Flash配置共享方案
OpenClaw多设备同步:GLM-4.7-Flash配置共享方案 1. 为什么需要多设备同步配置? 去年冬天,我在办公室和家里两台MacBook上分别部署了OpenClaw对接GLM-4.7-Flash模型。很快发现一个头疼的问题:每次在办公室调试好的技能参数&#…...
NXP S32K3xx之HSE密钥管理与安全服务实战
1. HSE密钥管理基础:从零开始理解安全引擎 第一次接触NXP S32K3xx的HSE模块时,我被各种密钥术语搞得晕头转向。经过几个实际项目的打磨,现在我可以负责任地告诉你:理解HSE密钥管理就像学习一门新语言,掌握基础词汇后就…...
Swin2SR惊艳效果展示:低清草稿图秒变4K高清作品集
Swin2SR惊艳效果展示:低清草稿图秒变4K高清作品集 你有没有遇到过这种情况?一张特别有感觉的AI生成图,可惜分辨率太低,放大后全是马赛克;一张珍贵的旧照片,像素模糊得看不清人脸;或者从网上找到…...
使用AI大大提升了学习代码的效率
最近看到一个观点,说AI的发展导致代码越来越不值钱了,AI降低了我们学习的门槛,大大提升了学习效率。好像很多程序都可以一个人一天上架一款产品。或许有夸张成分,但像我们普通人都体验到了AI的方便,比如在项目开发的过…...
告别加班!3个Word神技巧,文档处理快人一步
如影随形地跟着那堆积如山的文档,像学生名单,课程表,教学计划,家长通知等等,这些重复性工作着实耗费了大量精力。事实上,Word当中蕴含着好些能够让你达成事半功倍效果的技巧,一旦将它们掌握住&a…...
企业级 Agent SKILL 最佳实践
最近,真的是屁颠屁颠地使用Openclaw作为业务核心为客户打造智能体的工作流程,包括组织、业务、技术三个全面的转型。同时,由于OpenAI的Sora下线,年初刚刚建立的AI漫剧工作流,资产库以及提示词都需要转换成替代品。还有…...
TscanCode静态代码扫描工具原理与实践
嵌入式静态代码扫描工具TscanCode深度解析1. 静态代码分析技术概述1.1 静态代码扫描原理静态代码扫描是一种在不实际执行程序的情况下,通过词法分析、语法分析、控制流和数据流分析等技术对源代码进行检测的方法。这种技术能够有效识别代码中潜在的错误和缺陷&#…...
Python异步编程避坑:为什么你的‘async with’会报错?手把手教你正确使用aiohttp
Python异步编程避坑指南:深入理解aiohttp的正确打开方式 第一次接触Python异步编程时,很多人都会在async with这个语法上栽跟头。明明照着文档写的代码,运行时却抛出"SyntaxError: async with outside async function"的错误&#…...
ESLyric歌词源高效配置与避坑指南:Foobar2000用户进阶教程
ESLyric歌词源高效配置与避坑指南:Foobar2000用户进阶教程 【免费下载链接】ESLyric-LyricsSource Advanced lyrics source for ESLyric in foobar2000 项目地址: https://gitcode.com/gh_mirrors/es/ESLyric-LyricsSource ESLyric-LyricsSource是Foobar2000…...
