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

【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就无法申请该锁。

image-20230120163852340

相关文章:

【Linux】旋转锁 | 读写锁

在之前的线程学习中&#xff0c;用到的锁都是挂起等待锁&#xff0c;如果申请不到锁&#xff0c;那就会在锁中等待&#xff1b; 自旋锁则不大相似 文章目录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除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我们需要一种统一配置管理方案&#xff0c;可以集中管理…...

正则表达式引擎NFA自动机的回溯解决方案总结

前几天线上一个项目监控信息突然报告异常&#xff0c;上到机器上后查看相关资源的使用情况&#xff0c;发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具&#xff0c;我们导出了出问题的堆栈信息。 我们可以看到所有的堆栈都指向了一个名为 validateUrl 的方法&#…...

卷积神经网络之AlexNet

目录概述AlexNet特点激活函数sigmoid激活函数ReLu激活函数数据增强层叠池化局部相应归一化DropoutAlexnet网络结构网络结构分析AlexNet各层参数及其数量模型框架形状结构关于数据集训练学习keras代码示例概述 由于受到计算机性能的影响&#xff0c;虽然LeNet在图像分类中取得了…...

React中setState什么时候是同步的,什么时候是异步的?

本文内容均针对于18.x以下版本 setState 到底是同步还是异步&#xff1f;很多人可能都有这种经历&#xff0c;面试的时候面试官给了你一段代码&#xff0c;让你说出输出的内容&#xff0c;比如这样&#xff1a; constructor(props) {super(props);this.state {val: 0}}compo…...

优秀开源软件的类,都是怎么命名的?

日常编码中&#xff0c;代码的命名是个大的学问。能快速的看懂开源软件的代码结构和意图&#xff0c;也是一项必备的能力。 Java项目的代码结构&#xff0c;能够体现它的设计理念。Java采用长命名的方式来规范类的命名&#xff0c;能够自己表达它的主要意图。配合高级的 IDEA&…...

绘制CSP的patterns矩阵图

最近在使用FBCSP处理数据,然后就想着看看处理后的样子,用地形图的形式表现出来,但是没有符合自己需求的函数可以实现,就自己尝试的实现了一下,这里记录一下,方便以后查阅。 绘制CSP的patterns矩阵图 对数据做了FBCSP处理,但是想画一下CSP计算出来的patterns的地形图,并…...

Datatables展示数据(表格合并、日期计算、异步加载数据、分页显示、筛选过滤)

系列文章目录 datatable 自定义筛选按钮的解决方案Echarts实战案例代码(21)&#xff1a;front-endPage的CJJTable前端分页插件ajax分页异步加载数据的解决方案 文章目录系列文章目录前言一、html容器构建1.操作按钮2.表格构建二、时间日期计算三、dataTables属性配置1.调用2.过…...

Python decimal模块的使用

Python decimal 模块Python中的浮点数默认精度是15位。Decimal对象可以表示任意精度的浮点数。getcontext函数用于获取当前的context环境&#xff0c;可以设置精度、舍入模式等参数。#在context中设置小数的精度 decimal.getcontext().prec 100通过字符串初始化Decimal类型的变…...

pycharm常用快捷键

编辑类&#xff1a; Ctrl D 复制选定的区域或行 Ctrl Y 删除选定的行 Ctrl Alt L 代码格式化 Ctrl Alt O 优化导入&#xff08;去掉用不到的包导入&#xff09; Ctrl 鼠标 简介/进入代码定义 Ctrl / 行注释 、取消注释 Ctrl 左方括号 快速跳到代码开头 Ctrl 右方括…...

useCallback 与 useMemo 的区别 作用

useCallback 缓存钩子函数&#xff0c;useMemo 缓存返回值&#xff08;计算结果&#xff09;。 TS声明如下&#xff1a;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深度学习实战 | 计算机视觉

深度学习领域技术的飞速发展&#xff0c;给人们的生活带来了很大改变。例如&#xff0c;智能语音助手能够与人类无障碍地沟通&#xff0c;甚至在视频通话时可以提供实时翻译&#xff1b;将手机摄像头聚焦在某个物体上&#xff0c;该物体的相关信息就会被迅速地反馈给使用者&…...

力扣(LeetCode)436. 寻找右区间(2023.03.10)

给你一个区间数组 intervals &#xff0c;其中 intervals[i] [starti, endi] &#xff0c;且每个 starti 都 不同 。 区间 i 的 右侧区间 可以记作区间 j &#xff0c;并满足 startj > endi &#xff0c;且 startj 最小化 。 返回一个由每个区间 i 的 右侧区间 在 interv…...

已解决Servlet中Request请求参数中文乱码的问题

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…...

【flask】URL和视图映射

目录 首页 传参 URL数据类型 get传参 首页 url与视图函数的映射是通过app.route()装饰器实现的。 只有一个斜杠代表的是根目录——首页。 传参 URL传参是通过<参数名称>的形式进行传递。URL中有几个参数&#xff0c;在视图函数中也要指定几个参数 from flask im…...

Python实现性能测试(locust)

一、安装locustpip install locust -- 安装&#xff08;在pycharm里面安装或cmd命令行安装都可&#xff09;locust -V -- 查看版本&#xff0c;显示了就证明安装成功了或者直接在Pycharm中安装locust:搜索locust并点击安装&#xff0c;其他的第三方包也可以通过这种方式二、loc…...

【数论】试除法判断质数,分解质因数,筛质数

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 现已更新完KMP算法、排序模板&#xff0c;之…...

【C++】红黑树

文章目录红黑树的概念红黑树的性质特征红黑树结点的定义红黑树的插入操作情况1情况2情况3特殊情况代码实现红黑树的验证红黑树的删除红黑树和AVL树的比较红黑树的应用红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但是每一个结点都增加一个存储位表示结点的颜…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

【深度学习新浪潮】什么是credit assignment problem?

Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...

WinUI3开发_使用mica效果

简介 Mica(云母)是Windows10/11上的一种现代化效果&#xff0c;是Windows10/11上所使用的Fluent Design(设计语言)里的一个效果&#xff0c;Windows10/11上所使用的Fluent Design皆旨在于打造一个人类、通用和真正感觉与 Windows 一样的设计。 WinUI3就是Windows10/11上的一个…...