【Linux】读写锁和自旋锁

简介: 在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少;相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长,再给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。


一. 读写锁


1. 什么是读写锁?


在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少;相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长,再给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。


读写锁的主要功能是维护以下三种关系:


读读共享:读者可以同时访问临界资源。

写写互斥:写者之间择其一访问临界资源。

读写互斥:写者在写的时候读者不能访问临界资源,反之亦然。

这三种关系对读者、写者的具体作用效果体现在下面表格中:



当前锁的状态 读锁请求 写锁请求

无锁 可以 可以

读锁 可以 阻塞

写锁 阻塞 阻塞

读写锁的使用场景


场景一:写入操作少,读取操作多。

场景二:数据写入之后,剩下的操作就是读取。

PS:读写锁的设计是读锁优先。即当读者线程和写者线程同时去竞争同一把读写锁时,读者线程优先级更高。这样设计的目的是为了优先满足更多用户的需求,少数服从多数。


读者写者模式 VS 生产者消费者模式


生产者相当于写者。

消费者不同于读者。区别是消费者会取走数据,而读者不会。

读写锁概述

fc776e248afb4cc6ba9667be2fc57646.png



2. 为什么要有读写锁?


读写锁将操作分为读、写两种方式,可以多个线程同时占用读模式的读写锁,这样使得程序具有更高的并行性。


有时候,在多线程中,有一些公共数据修改的机会比较少,而读的机会却是非常多的,此公共数据的操作基本都是读,如果每次操作都给此段代码加锁,太浪费时间了而且也很浪费资源,降低程序的效率,因为读操作不会修改数据,只是做一些查询,所以在读的时候考虑不用给此段代码加锁,让读者可以共享的访问,只有涉及到写的时候,互斥的访问就好了。


3. POSIX下的读写锁相关接口函数

cd0f530f9de84a66a0dd883bd619f382.png



4. 读写锁实现原理


读写锁的本质是:互斥锁封装而成的写独占,读共享,读锁优先级高的锁。


下面是读写锁实现原理的伪代码:

c63c3d23693a49e2ba5e1daf687badb0.png


二. 自旋锁


1. 什么是自旋锁?

自旋锁(spinlock):是为实现保护临界资源而提出的一种轻量级锁机制,当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。


其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个线程能持有锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,则资源申请者只能进入睡眠状态(进行上下文切换和任务调度)。但是自旋锁不会引起申请者睡眠,如果自旋锁已经被别的线程持有,那么申请者就一直忙循环检查该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。


有个经典的例子来比喻自旋锁:在一个宿舍中,共用一个厕所,那么这个厕所就是临界资源,且在任一时刻最多只能有一个人在使用。当厕所闲置时,谁来了都可以使用。当甲在里面蹲坑时,就会关上厕所门,而乙也要使用,但是急啊,就得在门外焦急地等待,急得团团转,是为“自旋”。

ef7f250b2b4d43b28876d125aeb03157.png


至于互斥锁可以理解为还是那个宿舍的厕所,甲方正在里面洗澡,本来乙方也要洗,但是看到里面有人后决定先上床睡一觉,顺便等甲洗完后自己了再来洗,这里的睡一觉相当于线程进入休眠状态,睡之前的脱衣工作相当于线程的上下文切换和任务调度。



9a6a16bbc7fb4828a4fa1506d8168ca8.png

自旋锁特性


在任何时刻最多只能有一个线程获得自选锁。

要求持有锁的处理器所占用的时间尽可能短。

等待锁的线程进入忙循环,而不是休眠等待。

不需要进行上下文切换和任务调度。


2. 为什么要有自旋锁?


确定线程进入临界资源时间较短时可以考虑使用自旋锁,这样等待申请锁的线程一直处于忙循环的状态去监测锁资源是否被释放,而不是休眠等待,所以页不需要进行上下文切换和任务调度。


如果线程进入临界资源时间比较长的话,就不宜使用自旋锁,因为等待锁的线程会消耗过多CPU资源:如果申请不成功,申请者将一直循环判断,这无疑降低了CPU的使用率。这时用互斥锁比较好,它们让申请锁的线程进入睡眠等待状态。


PS:在实际使用时,不论是自旋锁还是互斥锁,我们看到的现象都是:申请不到锁的线程停止不动、被阻塞住了,实际上二者对线程的“阻塞方式”是不同的。


3. POSIX下的自旋锁相关接口函数


1b240bafb12a41beb4f56e56b384094c.png


4. 自旋锁实现原理

0f98bed9e2a2446e9f0f76a1552c2570.png

相关文章
|
21天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
1月前
|
算法 Linux 开发者
Linux内核中的锁机制:保障并发控制的艺术####
本文深入探讨了Linux操作系统内核中实现的多种锁机制,包括自旋锁、互斥锁、读写锁等,旨在揭示这些同步原语如何高效地解决资源竞争问题,保证系统的稳定性和性能。通过分析不同锁机制的工作原理及应用场景,本文为开发者提供了在高并发环境下进行有效并发控制的实用指南。 ####
|
1月前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
62 6
|
2月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
4月前
|
监控 关系型数据库 MySQL
在Linux中,mysql的innodb如何定位锁问题?
在Linux中,mysql的innodb如何定位锁问题?
|
3月前
|
Linux
linux内核 —— 读写信号量实验
linux内核 —— 读写信号量实验
|
4月前
|
运维 监控 Linux
Linux系统读写硬盘慢,如何排查?四步教你定位问题!
【8月更文挑战第24天】在Linux系统运维中,硬盘读写速度慢是一个常见且影响重大的问题。它不仅会导致服务器性能下降,还会影响用户体验。本文将详细介绍四个步骤,帮助你快速定位并解决Linux系统硬盘读写慢的问题。
871 0
|
6月前
|
监控 Linux 数据处理
lslocks:Linux系统中的锁信息查看利器
`lslocks`是Linux工具,用于查看系统上的文件锁信息,帮助诊断进程同步问题。它显示持有锁的进程、锁类型(如POSIX、flock)和状态。通过简洁的输出,用户能识别死锁和资源争用,优化性能。结合其他命令如`grep`和`awk`可增强分析能力。需适当权限运行,定期监控以预防并发访问问题,处理死锁时要谨慎。
|
6月前
|
API
Linux---线程读写锁详解及代码实现
Linux---线程读写锁详解及代码实现
|
5月前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
56 0