【Linux】读写锁和自旋锁

一. 读写锁

1. 什么是读写锁?

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

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

  • 读读共享:读者可以同时访问临界资源。
  • 写写互斥:写者之间择其一访问临界资源。
  • 读写互斥:写者在写的时候读者不能访问临界资源,反之亦然。

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

当前锁的状态读锁请求写锁请求
无锁可以可以
读锁可以阻塞
写锁阻塞阻塞

读写锁的使用场景

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

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

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

  • 生产者相当于写者。
  • 消费者不同于读者。区别是消费者会取走数据,而读者不会。

读写锁概述

在这里插入图片描述

2. 为什么要有读写锁?

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

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

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

在这里插入图片描述

4. 读写锁实现原理

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

下面是读写锁实现原理的伪代码:
在这里插入图片描述

二. 自旋锁

1. 什么是自旋锁?

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

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

有个经典的例子来比喻自旋锁:在一个宿舍中,共用一个厕所,那么这个厕所就是临界资源,且在任一时刻最多只能有一个人在使用。当厕所闲置时,谁来了都可以使用。当甲在里面蹲坑时,就会关上厕所门,而乙也要使用,但是急啊,就得在门外焦急地等待,急得团团转,是为“自旋”。
在这里插入图片描述
至于互斥锁可以理解为还是那个宿舍的厕所,甲方正在里面洗澡,本来乙方也要洗,但是看到里面有人后决定先上床睡一觉,顺便等甲洗完后自己了再来洗,这里的睡一觉相当于线程进入休眠状态,睡之前的脱衣工作相当于线程的上下文切换和任务调度。
在这里插入图片描述

自旋锁特性

  • 在任何时刻最多只能有一个线程获得自选锁。
  • 要求持有锁的处理器所占用的时间尽可能短。
  • 等待锁的线程进入忙循环,而不是休眠等待。
  • 不需要进行上下文切换和任务调度。

2. 为什么要有自旋锁?

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

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

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

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

在这里插入图片描述

4. 自旋锁实现原理

在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
随着信息技术的发展和数字化产品的普及,嵌入式系统的研究开发逐渐成为热点。而Linux又以其独特的优势成为嵌入式系统的主流。作为嵌入式系统和用户之桥梁的人机交互接口设备也是其中必不可少的一部分,用户与系统的交互是否准确和便捷极大地影响了嵌入式产品的竞争力。   本文对Unity805plus微处理器平台下人机交互接口设备驱动程序的设计开发做了深入的研究与实践。   Unity805plus微处理器是基于Unicore架构的新型32位移动终端应用处理器,面向低成本手持设备和其它通用嵌入式设备。本课题基于Linux2.4.19操作系统,设计和实现了在此平台下的人机交互接口设备驱动程序。   论文在介绍了嵌入式Linux下设备驱动层次结构、运行机制、编译平台方法以及字符设备驱动程序使用流程的基础上,针对Unity805plus此新型平台下键盘、触摸屏、LCD这三种人机交互设备提出了实际的驱动设计方案。其中:系统以中断方式来访问键盘和触摸设备,采用了Linux内核定时器并把任务放在后台执行以等待键盘或触摸中断事件,并运用了自旋锁、信号量、完成变量等内核同步方法;而LCD设备采用Unity805plus内置的LCD控制器与系统进行通讯,利用帧缓冲(framebuffer)设备作为接口,使上层应用程序能够在图形模式下直接对显示缓冲区进行统一的读写操作。文中按照驱动的设计流程为主线给出了各设备驱动程序的控制器设置、GPIO口设置、中断设置等关键部分的详细代码分析。   文中所述的设备驱动已经能够在Unity805plus平台的媒体播放器上稳定运行,并通过了初步的功能验证。   随着消费类电子产品的市场推陈出新所带来的巨大需求(如iPhone),相应的人机交互接口设备相关技术亦不断更新,比如新型的触摸屏技术或是将键盘、LCD等驱动电路集成在一种集成电路模块中等。因此,人机交互接口设备驱动的研究也将有广阔的前景。
Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户 /服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对象的持续性 24进程间通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量集结构 信号量集函数 信号量示例 31System V信号量(二) 用信号量实现进程互斥示例 32System V信号量(三) 用信号集解决哲学家就餐问题 33System V共享内存与信号量综合 用信号量解决生产者消费者问题 实现shmfifo 34POSIX消息队列 POSIX消息队列相关函数 POSIX消息队列示例 35POSIX共享内存 POSIX共享内存相关函数 POSIX共享内存示例 Linux网络编程之线程篇 36线程介绍 什么是线程 进程与线程 线程优缺点 线程模型 N:1用户线程模型 1:1核心线程模型 N:M混合线程模型 37POSIX线程(一) POSIX线程库相关函数 用线程实现回射客户/服务器 38POSIX线程(二) 线程属性 线程特定数据 39POSIX信号量与互斥锁 POSIX信号量相关函数 POSIX互斥锁相关函数 生产者消费者问题 自旋锁读写锁介绍 40POSIX条件变量 条件变量 条件变量函数 条件变量使用规范 使用条件变量解决生产者消费者问题 41一个简单的线程池实现 线程池性能分析 线程池实现 网络编程, Linux

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值