Golang中读写锁的底层实现

news2024/11/15 23:40:41

目录

Sync.RWMutex 背景与机制

接口简单介绍

sync.RWMutex 数据结构

读锁流程

RLock

RUnlock

 RWMutex.rUnlockSlow

写锁流程

Lock

Unlock


Sync.RWMutex 背景与机制

从逻辑上,可以把 RWMutex 理解为一把读锁加一把写锁;

写锁具有严格的排他性,当其被占用,其他试图取写锁或者读锁的 goroutine 均阻塞;

读锁具有有限的共享性,当其被占用,试图取写锁的 goroutine 会阻塞,试图取读锁的 goroutine 可与当前 goroutine 共享读锁;

RWMutex 适用于读多写少的场景

最理想化的情况,当所有操作均使用读锁,则可实现去无化;

最悲观的情况,倘若所有操作均使用写锁,则 RWMutex 退化为普通的 Mutex.

接口简单介绍

	var mtu sync.RWMutex
	mtu.RLock() //读锁 加锁

	mtu.RUnlock() //读锁 解锁

	mtu.Lock() //写锁 加锁

	mtu.Unlock() //写锁 解锁

sync.RWMutex 数据结构

const rwmutexMaxReaders = 1 << 30

type RWMutex struct {
    w           Mutex  // held if there are pending writers
    writerSem   uint32 // semaphore for writers to wait for completing readers
    readerSem   uint32 // semaphore for readers to wait for completing writers
    readerCount int32  // number of pending readers
    readerWait  int32  // number of departing readers
}

• rwmutexMaxReaders:共享读锁的 goroutine 数量上限,值为 2^29;

• w:RWMutex 内置的一把普通互斥锁 sync.Mutex;

• writerSem:关联写锁阻塞队列的信号量;

• readerSem:关联读锁阻塞队列的信号量;

• readerCount:正常情况下等于介入读锁流程的 goroutine 数量;当 goroutine 接入写锁流程时,该值为实际介入读锁流程的 goroutine 数量减 rwmutexMaxReaders.

• readerWait:记录在当前 goroutine 获取写锁前,还需要等待多少个 goroutine 释放读锁.

读锁流程

RLock

func (rw *RWMutex) RLock() {
    if atomic.AddInt32(&rw.readerCount, 1) < 0 {
        runtime_SemacquireMutex(&rw.readerSem, false, 0)
    }
}

1,基于原子操作,将 RWMutex 的 readCount 变量加一,表示占用或等待读锁的 goroutine 数加一

2,倘若 RWMutex.readCount 的新值仍小于 0,说明有 goroutine 未释放写锁

写锁加锁时 readCount 减去了 特殊值 (const rwmutexMaxReaders = 1 << 30)

因此将当前 goroutine 添加到读锁的阻塞队列中并阻塞挂起.

RUnlock

func (rw *RWMutex) RUnlock() {
    if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
        rw.rUnlockSlow(r)
    }
}

1,基于原子操作,将 RWMutex 的 readCount 变量加一,表示占用或等待读锁的 goroutine 数减一

2,倘若 RWMutex.readCount 的新值小于 0,说明有 goroutine 在等待获取写锁,

则走入 RWMutex.rUnlockSlow(唤醒阻塞等待的写锁) 的流程中. 

 RWMutex.rUnlockSlow

func (rw *RWMutex) rUnlockSlow(r int32) {
    if r+1 == 0 || r+1 == -rwmutexMaxReaders {
        fatal("sync: RUnlock of unlocked RWMutex")
    }
    if atomic.AddInt32(&rw.readerWait, -1) == 0 {
        runtime_Semrelease(&rw.writerSem, false, 1)
    }
}

1,对 RWMutex.readerCount 进行校验,倘若发现当前协程此前未抢占过读锁(并为对读锁+1),或

介入读锁流程的goroutine 数量达到上限(r+1复原后等于特殊值,则唤醒前已经有写锁)抛出fatal

2.基于原子操作,对 RWMutex.readerWait 进行减一操作,倘若其新值为 0,说明当前 goroutine 是最后一个介入读锁流程的协程,因此需要唤醒一个等待写锁的阻塞队列的 goroutine. 

写锁流程

Lock

func (rw *RWMutex) Lock() {
    rw.w.Lock()
    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
    if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
        runtime_SemacquireMutex(&rw.writerSem, false, 0)
    }
}
  • 对 RWMutex 内置的互斥锁进行加锁操作;

  • 基于原子操作,对 RWMutex.readerCount(标识作用)进行减少-rwmutexMaxReaders 的操作;

  • 倘若此时存在未释放读锁的 gouroutine则基于原子操作在 RWMutex.readerWait 的基础上加上介入读锁流程的 goroutine 数量(负责更新),并将当前 goroutine 添加到写锁的阻塞队列中挂起.

Unlock

func (rw *RWMutex) Unlock() {
    r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
    if r >= rwmutexMaxReaders {
        fatal("sync: Unlock of unlocked RWMutex")
    }
    for i := 0; i < int(r); i++ {
        runtime_Semrelease(&rw.readerSem, false, 0)
    }
    rw.w.Unlock()
}

1,基于原子操作,将 RWMutex.readerCount 的值加上 rwmutexMaxReaders;(复原)

2,倘若发现 RWMutex.readerCount 的新值大于 rwmutexMaxReaders,则说明

要么当前 RWMutex 未上过写锁,要么介入读锁流程的 goroutine 数量已经超限,因此直接抛出 fatal

3,因此唤醒读锁阻塞队列中的所有 goroutine;

(可见,读锁比写锁先被唤醒,竞争读锁的 goroutine 更具备优势)

4,解开 RWMutex 内置的互斥锁.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1928572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring之事务管理TranscationManager(大合集)

原子性 事务是数据库的逻辑工作单位&#xff0c;事务中包括的诸操作要么全做&#xff0c;要么全不做。 一致性 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 隔离性 一个事务的执行不能被其他事务干扰。 持续性 一…

人工智能算法工程师(中级)课程12-PyTorch神经网络之LSTM和GRU网络与代码详解1

大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(中级)课程12-PyTorch神经网络之LSTM和GRU网络与代码详解。在深度学习领域,循环神经网络(RNN)因其处理序列数据的能力而备受关注。然而,传统的RNN存在梯度消失和梯度爆炸的问题,这使得它在长序列任务中的表现不尽…

【Git分支管理】理解分支 | 创建分支 | 切换分支 | 合并分支 | 删除分支 | 强制删除分支

目录 前言 0.理解分支 1.查看本地仓库存在的分支 2.HEAD指向分支 3.创建本地分支 4.切换分支 5.分支提交操作 6.合并分支 快进模式Fast-forward 7.删除分支 8.强制删除分支 本篇开始介绍下Git提供的杀手级的功能&#xff1a;分支管理 先提交再合并 前言 在玄幻武侠…

IP-Guard日志数据上传至 SYSLOG 服务器操作指南

一、功能简介 服务器支持把日志数据上传到 SYSLOG 服务器。 二、功能配置 2.1 数据目录移交设置 在服务器安装目录下 OServer3.ini 文件中&#xff0c;添加工具启动配置&#xff0c;配置五分钟内生效。 Path&#xff1a;设置移交目录路径&#xff0c;IPG 服务器会把收集完成的…

Redis④ —— 高可用

1. 主从复制 一主多从模式&#xff0c;采用读写分离的方式主服务器可以进行读写操作&#xff0c;当发生写操作时自动将写操作同步给从服务器&#xff0c;而从服务器一般是只读&#xff0c;并接受主服务器同步过来写操作命令&#xff0c;然后执行这条命令。主从服务器之间的命令…

jvm常用密令、jvm性能优化、jvm性能检测、Java jstat密令使用、Java自带工具、Java jmap使用

1.jps是Java虚拟机的进程状态工具&#xff0c;用于列出正在运行的Java进程 jps命令的使用&#xff1a;cmd打开直接jps 1.1不带参数&#xff1a; jps 默认情况下&#xff0c;列出所有正在运行的 Java 进程的进程 ID 和主类名。 1.2 -l&#xff1a;显示完整的主类名或 JAR 文件…

《故障复盘 · 数据库连接异常关闭》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

不开放80或443端口也能申请IP SSL证书!

在申请SSL/HTTPS证书时&#xff0c;如果不方便使用域名或者没有域名&#xff0c;就要申请一种特殊的SSL证书——IP SSL证书。但是一般的IP地址证书签发过程中&#xff0c;需要短暂开放80或者443端口才能签发成功。那么问题来了&#xff0c;有的实在不能开放80或者443端口&#…

keil中GD32 MCU IAP中APP的存储地址如何设置?

前面和大家聊过什么是IAP&#xff0c;那么IAP中APP的存储地址该如何设置呢&#xff1f; 以keil为例&#xff0c;打开工程的option选项卡&#xff1a; 将IROM1中的地址改为你想要保存的位置&#xff0c;比如0x08008000开始的位置&#xff1a; 这样通过keil烧录&#xff0c;程序…

记录|.NET上位机开发和PLC通信的实现

本文记录源自&#xff1a;B站视频 实验结果&#xff1a;跟视频做下来是没有问题的。能运行。 目录 前言一、项目Step1. 创建项目Step2. 创建动态图片展示Step3. 创建图片型按钮Step4. 创建下拉框Step1~4的效果展示Step5. 编程实体类操作类Main函数 Step1~5的效果展示Main函数 最…

[Python学习篇] Python PyMysql

什么是PyMysql PyMysql是一个纯 Python 实现的 MySQL 客户端库&#xff0c;允许你在 Python 程序中与 MySQL 数据库进行交互。 安装PyMysql PyMysql地址&#xff1a;https://pypi.org/project/PyMySQL/ pip install pymysql 使用PyMysql 连接mysql import pymysql# 数据库连…

数据库redis命令作业九

1、安装redis&#xff0c;启动客户端、验证。 2、string类型数据的命令操作&#xff1a; &#xff08;1&#xff09; 设置键值&#xff1a; &#xff08;2&#xff09; 读取键值&#xff1a; &#xff08;3&#xff09; 数值类型自增1&#xff1a; &#xff08;4&#xff09; 数…

【单片机毕业设计选题24064】-基于阿里云的鱼塘水质检测系统

系统功能: 主控为STM32F103C8T6&#xff0c;通过PH值传感器、浑浊度传感器、温度传感器采集各项水质数据&#xff0c;系统可设定各参数 的阈值&#xff0c;超过设定的阈值将通过蜂鸣器响来提醒用户做出措施&#xff0c;同时通过ESP-12F WIFI模块将设备连接阿里云 物联网平台…

信通院全景图发布 比瓴科技领跑软件供应链安全,多领域覆盖数字安全服务

近日&#xff0c;中国信息通信研究院在2024全球数字经济大会—数字安全生态建设专题论坛正式发布首期《数字安全护航技术能力全景图》&#xff08;以下简称全景图&#xff09;。 比瓴科技入选软件供应链安全赛道“开发流程安全管控、交互式安全测试、静态安全测试、软件成分分…

rtf是什么格式的文件?rtf格式和word的区别是什么?

RTF是什么格式的文件? RTF&#xff08;富文本格式&#xff0c;Rich Text Format&#xff09;和Word文档&#xff08;以.doc和.docx为扩展名的Microsoft Word文档&#xff09;是两种常用的文本文件格式。它们在文件结构、兼容性、功能和使用场景等方面存在一些显著差异。 比如…

泰迪智能科技江西大数据实验室成功案例介绍说明

高校大数据实验室作为作为支撑高校人培方案实施的核心设施&#xff0c;实验室的建设一定要与学科建设、人才培养充分融合&#xff0c;是一个包含物理空间硬件资源软件资源课程内容的系统化工程。高校在实验室规划过程中&#xff0c;第一要务就是从学科定位出发、结合学校的特色…

ASP.NET MVC-制作可排序的表格组件-PagedList版

环境&#xff1a; win10 参考&#xff1a; 学习ASP.NET MVC(十一)——分页 - DotNet菜园 - 博客园 https://www.cnblogs.com/chillsrc/p/6554697.html ASP.NET MVCEF框架实现分页_ef 异步分页-CSDN博客 https://blog.csdn.net/qq_40052237/article/details/106599528 本文略去…

分布式IO系统BL201 Profinet耦合器

BL201耦合器是一个数据采集和控制系统&#xff0c;基于强大的32 位微处理器设计&#xff0c;采用Linux操作系统&#xff0c;是一种模块化的分布式I/O系统。该系统由3部分组成&#xff1a;现场总线耦合器和各种类型的&#xff08;数字和模拟信号以及特殊功能&#xff09;I/O模块…

部署k8s 1.28.9版本

继上篇通过vagrant与virtualBox实现虚拟机的安装。笔者已经将原有的vmware版本的虚拟机卸载掉了。这个场景下&#xff0c;需要重新安装k8s 相关组件。由于之前写的一篇文章本身也没有截图。只有命令。所以趁着现在。写一篇&#xff0c;完整版带截图的步骤。现在行业这么卷。离…

C#与倍福Plc通信——使用仿真软件模拟倍福PLC运行

前言 我们在编写上位机与倍福PLC通信的过程中,有时候我们没有真实的Plc,但是我们又想提前测试与倍福PLC的通信,那么这个时候我们就可以使用倍福的仿真软件模拟PLC,然后我们上位机就可以与仿真PLC进行通信了,下面进行详细介绍: 1、下载并安装倍福PLC编程软件TwinCAT 安…