Golang中读写锁的底层实现

目录

Sync.RWMutex 背景与机制

接口简单介绍

sync.RWMutex 数据结构

读锁流程

RLock

RUnlock

 RWMutex.rUnlockSlow

写锁流程

Lock

Unlock


Sync.RWMutex 背景与机制

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

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

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

RWMutex 适用于读多写少的场景

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

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

接口简单介绍

	var mtu sync.RWMutexmtu.RLock() //读锁 加锁mtu.RUnlock() //读锁 解锁mtu.Lock() //写锁 加锁mtu.Unlock() //写锁 解锁

sync.RWMutex 数据结构

const rwmutexMaxReaders = 1 << 30type RWMutex struct {w           Mutex  // held if there are pending writerswriterSem   uint32 // semaphore for writers to wait for completing readersreaderSem   uint32 // semaphore for readers to wait for completing writersreaderCount int32  // number of pending readersreaderWait  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) + rwmutexMaxReadersif 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.mzph.cn/web/47107.shtml

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

相关文章

【python】OpenCV—Extreme Points in the Contour

文章目录 1、需求描述2、功能实现3、更多的例子4、完整代码5、参考 1、需求描述 给一张图片&#xff0c;找出其轮廓&#xff0c;并画出轮廓的上下左右极值点 输入图片 输出效果 2、功能实现 # 导入必要的包 import imutils import cv2 # 加载图像&#xff0c;将其转换为灰度…

JavaSE从零开始到精通(六) - 多态

1. 前言 现实中的多态&#xff1a;同一个对象在不同时刻展现出的不同形态&#xff01; 编程中的多态&#xff1a; 对于具体的类型和调用的方法&#xff0c;在编程时并不确定&#xff0c;而是在程序运行期间才确定&#xff0c;这样&#xff0c;不用修改源程序代码&#xff0c;…

git rebase 和 git merge区别

# 在自己的分支feature上开发&#xff0c;从master上拉最新改动, # 避免自己的分支落后master git checkout feature git rebase master1. 合并方式 merge&#xff1a;合并操作。它会取出一个公共的祖先节点&#xff0c;然后尝试将两个分支从该节点开始发生的所有变化都合并到…

【网络】linux实现双网卡热备:优先走A,故障后走B

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

vue3 + antd vue 纯前端 基于xlsx 实现导入excel 转 json,将json数据转换XLSX并下载(下载模版)

一、导入 0、关键代码 // 安装插件 npm i xlsx/yarn add xlsx // 导入xlsx import * as XLSX from xlsx; 点击提交的时候才整理数据。上传的时候文件保存在 state.form.file[0] 中的 // 定义字段映射关系 const fieldMap {sheet2json: {技能名称: skill_name,技能等级: …

uni-app 影视类小程序开发从零到一 | 开源项目分享

引言 在数字娱乐时代&#xff0c;移动设备已成为我们生活中不可或缺的一部分&#xff0c;尤其是对于电影爱好者而言&#xff0c;随时随地享受精彩影片成为一种日常需求。爱影家&#xff0c;一款基于 uni-app 开发的影视类小程序&#xff0c;正是为此而生。它不仅提供了丰富的影…

栈的概念—函数调用

也是一块内存空间&#xff0c;CPU的SP寄存器指向它&#xff0c;可用于函数调用、局部变量、多任务系统里保存现场 每个任务都有自己的栈

【Django+Vue3 线上教育平台项目实战】购物车与订单模块的精简实现与数据安全策略

文章目录 前言一、购物车模块1.后端核心逻辑2.前端页面代码3.操作流程及演示 二、订单模块1.订单模块模型类设计1.展示订单信息a.页面展示b.前端核心代码c.后端核心逻辑 2.订单是否使用优惠券与积分a.页面展示b.前端核心代码 3.订单支付方式a.页面展示b.前端核心代码 4.提交订单…

Perl脚本的魔法:打造自定义文件系统视图

Perl脚本的魔法&#xff1a;打造自定义文件系统视图 在文件管理与自动化任务中&#xff0c;Perl以其强大的文本处理能力而闻名。通过Perl&#xff0c;我们可以轻松实现自定义的文件系统视图&#xff0c;以满足特定的需求。这不仅可以帮助我们更好地组织和访问文件&#xff0c;…

PyTorch Autograd内部实现

原文&#xff1a; 克補 爆炸篇 25s (youtube.com) 必应视频 (bing.com)https://www.bing.com/videos/riverview/relatedvideo?&qPyTorchautograd&qpvtPyTorchautograd&mid1B8AD76943EFADD541E01B8AD76943EFADD541E0&&FORMVRDGAR 前面只要有一个node的re…

北京交通大学《深度学习》专业课,实验3卷积、空洞卷积、残差神经网络实验

一、实验要求 1. 二维卷积实验&#xff08;平台课与专业课要求相同&#xff09; ⚫ 手写二维卷积的实现&#xff0c;并在至少一个数据集上进行实验&#xff0c;从训练时间、预测精 度、Loss变化等角度分析实验结果&#xff08;最好使用图表展示&#xff09; ⚫ 使用torch.nn…

Matlab基础语法篇(下)

Matlab基础语法&#xff08;下&#xff09; 一、逻辑基础&#xff08;一&#xff09;逻辑运算符&#xff08;二&#xff09;all、any、find函数&#xff08;三&#xff09;练习 二、结构基础&#xff08;一&#xff09;条件结构&#xff08;1&#xff09;if-elseif-else-end&am…

Android构建任务assemble、bundle、compile、package、install

1. assemble 开头的任务&#xff1a; assembleDebug&#xff1a;构建 debug 版本的 APK 文件。assembleRelease&#xff1a;构建 release 版本的 APK 文件。assembleAndroidTest&#xff1a;构建测试 APK 文件&#xff0c;用于测试应用程序。assembleAndroidTestDebug&#xf…

十、操作符详解

目录 1、操作符分类 2、二进制转换 2.1二进制转十进制 2.1.1、十进制转二进制 2.2、二进制转八进制和十六进制 2.2.1、二进制转八进制 2.2.2、二进制转十六进制 3、原码、反码、补码 4、移位操作符&#xff08;移动的是二进制位&#xff09; 4.1、左移操作符 4.2、右…

VMware虚拟机下安装Ubuntu(详细教程,最小系统的安装,含VMware Tools)

1.VM的下载安装 VMware的下载安装教程_vm16 pro下载-CSDN博客 2. Ubuntu 下载 在官网或者镜像站下载所需版本的.ios镜像&#xff0c;这个镜像在接下来的步骤中会用到&#xff1a; Ubuntu 22.04.4 LTS 下载 和 清华大学开源软件镜像站 - Ubuntu 22.04.4 下载 3. 创建虚拟机 […

【C语言】深入解析希尔排序

文章目录 什么是希尔排序&#xff1f;希尔排序的基本实现代码解释希尔排序的优化希尔排序的性能分析希尔排序的实际应用结论 在C语言编程中&#xff0c;希尔排序是一种高效的排序算法&#xff0c;是插入排序的一种更高效的改进版本。它通过比较相距一定间隔的元素来进行排序&am…

智能水果保鲜度检测:基于YOLO和深度学习的完整实现

引言 水果新鲜程度直接影响其口感和营养价值。为了提高水果品质管理的效率和准确性&#xff0c;本文介绍了一种基于深度学习的水果新鲜程度检测系统。该系统包括用户界面&#xff0c;利用YOLO&#xff08;You Only Look Once&#xff09;v8/v7/v6/v5模型进行水果新鲜程度检测&…

RocketMQ中概念知识点记录 和 与SpringBoot集成实现发送 同步、异步、延时、批量、tag、key、事务消息等

1. 消息模型 消息&#xff08;Message&#xff09;: 是 RocketMQ 中数据传输的基本单位&#xff0c;由主题、标签、键值、消息体等组成。主题&#xff08;Topic&#xff09;: 消息的分类&#xff0c;类似于邮件的主题&#xff0c;用于对消息进行粗粒度的分类。标签&#xff08…

Zookeeper是什么,为什么要用,怎么用?

关于Zookeeper的全面了解与应用 前言&#xff1a;这几天在开发过程中&#xff0c;遇到了zk相关的一些问题&#xff0c;大体先复习下 Zookeeper作为分布式系统中的协调服务&#xff0c;起着至关重要的角色。本篇文章将从以下几个方面详细讲解什么是Zookeeper&#xff0c;为什么…

【Go系列】RPC和grpc

承上启下 介绍完了Go怎么实现RESTFul api&#xff0c;不可避免的&#xff0c;今天必须得整一下rpc这个概念。rpc是什么呢&#xff0c;很多人都想把rpc和http一起对比&#xff0c;但是他们不是一个概念。RPC是一种思想&#xff0c;可以基于tcp&#xff0c;可以基于udp也可以基于…