go 源码解读 sync.RWMutex

sync.RWMutex

    • 简介
    • 源码
      • 结构
      • RLock
      • RUnlock
      • Unlock
      • go 运行时方法

简介

简述sync包中读写锁的源码。 (go -version 1.21)

读写锁(RWMutex)是一种并发控制机制,用于在多个 goroutine 之间对共享资源进行读写操作。它提供了两种锁定方式:读锁和写锁。
读锁(RLock):多个 goroutine 可以同时持有读锁,而不会阻塞彼此。只有当没有写锁被持有时,读锁才会被授予。这样可以实现多个 goroutine 并发地读取共享资源,提高程序性能。
写锁(Lock):写锁是排它的,当某个 goroutine 持有写锁时,其他所有 goroutine 都无法获得读锁或写锁。这是为了确保在写入共享资源时,没有其他 goroutine 在读或写该资源。

写锁是排他性的 :
(假设写锁不是排他性的, 新的读锁可以被获取) 反证:(多个写锁的情况下就不聊了)
现在有 一个goroutine1 获取读锁; 一个 goroutine2 获取写锁,但没有写锁被其他goroutine持有
然后有个goroutine3 想获取读锁,在假设的条件下 goroutine3 就会获取到读锁, 会导致goroutine2 无法获取写锁, 极端情况下会导致goroutine2 一直获取不到写锁 ---- 写锁饥饿
所以 为了读写公平, 还是把写锁优先级提高, 在写锁的情况下, 新的读锁无法被获取。

源码

结构

// RWMutex 结构体包含了用于读写互斥锁的各种状态和信号量。在 RWMutex 中,多个 goroutine 可以同时持有读锁,
// 但只有一个 goroutine 可以持有写锁。RWMutex 的实现确保了在写锁等待的情况下,新的读锁无法被获取,
// 从而防止了读锁长时间等待
type RWMutex struct {w           Mutex        // 用于写锁的互斥锁writerSem   uint32       // 写锁的信号量,用于等待读锁完成readerSem   uint32       // 读锁的信号量,用于等待写锁完成readerCount atomic.Int32 // 读者持有读者锁的数量readerWait  atomic.Int32 // 写者等待读锁的数量
}
// readerCount 
// 当读者加锁时, readerCount  +1 
// 当读者解锁时, readerCount  -1
// 当 readerCount 大于 0 时,表示有读者持有读锁。
// 如果 readerCount 等于 0,则表示当前没有读者持有读锁。
// 当 readerCount 等于 0 时,其他写者可以尝试获取写锁
// 当 readerCount < 0 , 说明有写者在等待, 读者需要等待写者释放写锁
// readerWait(写者等待读锁的数量):
// 当写者尝试获取写锁,但当前有读者持有读锁时,写者会被阻塞,并且 readerWait 会增加。
// 当读者释放读锁时,如果有写者在等待读锁,readerWait 会减少,并且可能唤醒等待的写者
// readerCount > 0:表示有读者持有读锁。
// readerCount == 0 且 readerWait > 0:表示有写者在等待读锁,阻塞中。
// readerCount == 0 且 readerWait == 0:表示当前没有读者持有读锁,且没有写者在等待读锁。此时其他写者可以尝试获取写锁。
// 这样的设计是为了在写者等待读锁时,不允许新的读者加入,以确保写者获得更公平的机会。

RLock

在这里插入图片描述

// Happens-before relationships are indicated to the race detector via:
// - Unlock  -> Lock:  readerSem
// - Unlock  -> RLock: readerSem
// - RUnlock -> Lock:  writerSem
//
// happends-before 用于描述时间发生的顺序,在 这里 (代码中的注释)
// Unlock 事件发生之前(happens-before)的 Lock 事件。
// Unlock 事件发生之前(happens-before)的 RLock 事件。
// RUnlock 事件发生之前(happens-before)的 Lock 事件func (rw *RWMutex) RLock() {if race.Enabled {_ = rw.w.staterace.Disable()}// 它将 readerCount 增加 1。如果结果小于 0,说明有一个写者拿着锁了, 这边需要等着if rw.readerCount.Add(1) < 0 {// A writer is pending, wait for it.// 在读写锁的情况下 执行锁的信号量 // 实际得等待操作, 调用底层代码, 等写者释放锁runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}

race.Enabled : 竞态检测, 是go运行时提供的工具, 用于检测并发程序中的数据竞争问题,
使用 go run -race main.go 可以检测, 然后输出报告。
后面竞态检测代码 不说明

RUnlock

在这里插入图片描述

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {... // 竞态规则逻辑// readerCount 用于记录当前持有读锁的读者数量。如果读者计数减为负数,说明存在写者正在等待读锁释放if r := rw.readerCount.Add(-1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}... // 竞态规则逻辑
}
func (rw *RWMutex) rUnlockSlow(r int32) {... // 竞态规则逻辑// A writer is pending.// 减少读者等待计数。readerWait 记录正在等待读锁释放的读者数量。如果读者等待计数减为零,// 说明最后一个读者已经释放了读锁,可以唤醒等待的写者if rw.readerWait.Add(-1) == 0 {// The last reader unblocks the writer.// 释放写者的信号量runtime_Semrelease(&rw.writerSem, false, 1)}
}

在这里插入图片描述

func (rw *RWMutex) Lock() {... // 竞态规则逻辑// First, resolve competition with other writers.// 获取写锁,解决与其他写者的竞争rw.w.Lock()// Announce to readers there is a pending writer.// 将 readerCount 减去 rwmutexMaxReaders,然后再加上 rwmutexMaxReaders,目的是将 readerCount 设置为负数,// 表示有一个写者正在等待写锁。这会通过 readerWait 记录下来,并用于通知读者和写者。// 这边得  rw.readerCount 是一个负数, 在RLock 中有个判断 rw.readerCount <0 , // 这一段就是实现了写者优先, 不管当前有没有读者拿着读锁, 接下来拿锁的读锁, 统统排我后面,不能影响我(写者), 等我(写者)处理完了再说r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.// 如果有活跃的读者,且将 readerWait 增加 r 后不为零,说明有读者正在等待读锁释放if r != 0 && rw.readerWait.Add(r) != 0 {// 获取写锁。// 它不是马上就能获取写锁,而是可能会被阻塞,需要等待写锁的释放// 当写者执行这个操作时,如果当前没有其他写者或读者持有锁,那么它会成功获取写锁,否则它会被阻塞,直到没有其他写者或读者持有锁runtime_SemacquireRWMutex(&rw.writerSem, false, 0)}... // 竞态规则逻辑
}

Unlock

在这里插入图片描述

// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {... // 竞态规则逻辑// Announce to readers there is no active writer.// 将 readerCount 增加 rwmutexMaxReaders,用于通知活跃的读者,写者已经释放了写锁// 我( 写者)处理完了, 把 rw.readerCount 加回来了, 当前写锁写完了, 刚刚我(写者)拿锁以后 申请的读锁们, 可以唤醒了r := rw.readerCount.Add(rwmutexMaxReaders)... // 竞态规则逻辑// Unblock blocked readers, if any.// 遍历并逐个释放等待的读者,通过 runtime_Semrelease 信号量操作通知它们。for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// Allow other writers to proceed.rw.w.Unlock()... // 竞态规则逻辑
}

go 运行时方法

1、runtime_SemacquireRWMutexR(&rw.readerSem, false, 0):

这个方法用于获取读锁。
&rw.readerSem 是一个信号量,表示等待读锁的读者队列。
false 表示不以 LIFO(LastIn, First Out)模式进行等待队列的管理。 、
0 表示无超时。

2、runtime_SemacquireRWMutex(&rw.writerSem, false, 0):

这个方法用于获取写锁。
&rw.writerSem 是一个信号量,表示等待写锁的写者队列。
false 表示不以 LIFO模式进行等待队列的管理。
0 表示无超时。

3、runtime_Semrelease(&rw.writerSem, false, 1):

这个方法用于释放写锁。
&rw.writerSem 是写锁等待队列的信号量。
false 表示不以 LIFO 模式进行等待队列的管理。
1 表示释放一个写者,通知等待的写者。

4、runtime_Semrelease(&rw.readerSem, false, 0):

这个方法用于释放读锁。
&rw.readerSem 是读锁等待队列的信号量。
false 表示不以 LIFO 模式进行等待队列的管理。
0表示释放一个读者,通知等待的读者

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

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

相关文章

浅谈WPF之控件模板Control Template和数据模板Data Template

WPF不仅支持传统的Windows Forms编程的用户界面和用户体验设计&#xff0c;同时还推出了以模板为核心的新一代设计理念。在WPF中&#xff0c;通过引入模板&#xff0c;将数据和算法的“内容”和“形式”进行解耦。模板主要分为两大类&#xff1a;数据模板【Data Template】和控…

《MySQL系列-InnoDB引擎01》MySQL体系结构和存储引擎

文章目录 第一章 MySQL体系结构和存储引擎1 数据库和实例2 MySQL配置文件3 MySQL数据库路径4 MySQL体系结构5 MySQL存储引擎5.1 InnoDB存储引擎5.2 MyISAM存储引擎5.3 NDB存储引擎5.4 Memory存储引擎5.5 Archive存储引擎5.6 Federated存储引擎 6 连接MySQL6.1 TCP/IP6.2 命名管…

关于“Python”的核心知识点整理大全48

目录 world_population.py 16.2.5 制作世界地图 americas.py 16.2.6 在世界地图上呈现数字数据 na_populations.py 16.2.7 绘制完整的世界人口地图 world_population.py 16.2.8 根据人口数量将国家分组 world_population.py 16.2.9 使用 Pygal 设置世界地图的样式 w…

docker小白第九天

docker小白第九天 安装redis集群 cluster(集群)模式-docker版本&#xff0c;哈希槽分区进行亿级数据存储。如果1~2亿条数据需要缓存&#xff0c;请问如何设计这个存储案例。单机存储是不可能的&#xff0c;需要分布式存储&#xff0c;如果使用redis又该如何部署。 哈希取余分…

数据结构——红黑树 and B-树

红黑树 根据平衡条件第4、5两点 最短路径&#xff0c;都是黑色 最长路径&#xff0c;红黑相间 最长是最短的两倍 B-树

k8s-cni网络 10

Flannel vxlan模式跨主机通信原理 在同一个节点上的pod 流量通过cni网桥可以直接进行转发&#xff1b; 在需要跨主机访问时&#xff0c;数据包通过flannel(隧道) 知道另一边的mac地址&#xff0c;就可以拿到另一边的ip地址&#xff0c;然后构建常规的以太网数据包&#xff0c;…

泛目录是干什么用的蚂蚁seo泛程序

泛目录是干什么用的蚂蚁seo泛程序目录 泛目录是一种常见的网站优化方法&#xff0c;属于黑帽技术的一种。它的核心原理是利用高权重的网站继承目录&#xff0c;然后快速获得收录与排名。这种方法可以帮助网站在搜索引擎中获得更好的排名&#xff0c;从而吸引更多的流量。 泛目…

白话机器学习的数学-1-回归

1、设置问题 投入的广告费越多&#xff0c;广告的点击量就越高&#xff0c;进而带来访问数的增加。 2、定义模型 定义一个函数&#xff1a;一次函数 y ax b &#xff08;a 是斜率、b 是截距&#xff09; 定义函数&#xff1a; 3、最小二乘法 例子&#xff1a; 用随便确定的参…

flutter学习-day21-使用permission_handler进行系统权限的申请和操作

文章目录 1. 介绍2. 环境准备2-1. Android2-2. iOS 3. 使用 1. 介绍 在大多数操作系统上&#xff0c;权限不是在安装时才授予应用程序的。相反&#xff0c;开发人员必须在应用程序运行时请求用户的许可。在 flutter 开发中&#xff0c;则需要一个跨平台(iOS, Android)的 API 来…

计算机网络:知识回顾

0 本节主要内容 问题描述 解决思路 1 问题描述 通过一个应用场景来回顾计算机网络涉及到的协议&#xff08;所有层&#xff09;。如下图所示场景&#xff1a; 学生Bob将笔记本电脑用一根以太网电缆连接到学校的以太网交换机&#xff1b;交换机又与学校的路由器相连&#xf…

scratch打砖块游戏 2023年12月中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析

scratch打砖块游戏 2023年12月电子学会图形化编程Scratch等级考试三级真题 一、题目要求 1、准备工作 (1)删除小猫角色,选择角色小球Ball、砖块Button3和球板Paddle (2)选择背景Stars,在背景底端绘制一条红色的线段 (3)建立一个变量“分数” 2、功能实现 (1)…

GoLang学习之路,对Elasticsearch的使用,一文足以(包括泛型使用思想)(二)

书写上回&#xff0c;上回讲到&#xff0c;Elasticsearch的使用前提即&#xff1a;语法&#xff0c;表结构&#xff0c;使用类型结构等。要学这个必须要看前面这个&#xff1a;GoLang学习之路&#xff0c;对Elasticsearch的使用&#xff0c;一文足以&#xff08;包括泛型使用思…

【STM32】程序在SRAM中运行

程序在RAM中运行 1、配置内存分配。 2、修改跳转文件 FUNC void Setup(void) { SP _RDWORD(0x20000000); PC _RDWORD(0x20000004); } LOAD RAM\Obj\Project.axf INCREMENTAL Setup(); 3、修改下载ROM地址和RAM地址&#xff1b; 中断向量表映射 中断向量表映射到SRA…

应用在网络摄像机领域中的国产音频ADC芯片

IPC&#xff1a;其实叫“网络摄像机”&#xff0c;是IP Camera的简称。它是在前一代模拟摄像机的基础上&#xff0c;集成了编码模块后的摄像机。它和模拟摄像机的区别&#xff0c;就是在新增的“编码模块”上。模拟摄像机&#xff0c;顾名思义&#xff0c;输出的是模拟视频信号…

Apache DolphinScheduler 3.1.9 版本发布:提升系统的稳定性和性能

&#x1f680;我们很高兴宣布&#xff0c;Apache DolphinScheduler 的最新版本 3.1.9 已正式发布&#xff01;此版本在 3.1.8 的基础上进行了关键的 bug 修复和文档更新&#xff0c;共计修复了 14 个 bug 和改进了 3 个文档。 主要更新亮点 本次更新重点解决了以下几个关键问题…

阿里云30个公共云地域、89个可用区、5个金融云和政务云地域

阿里云基础设施目前已面向全球四大洲&#xff0c;公共云地域开服运营30个公共云地域、89个可用区&#xff0c;此外还拥有5个金融云、政务云地域&#xff0c;并且致力于持续的新地域规划和建设&#xff0c;从而更好的满足用户多样化的业务和场景需求。伴随着基础设施的加速投入和…

AI大模型引领未来智慧科研暨丨ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

Unity Window安装包制作

Unity Window安装包制作 介绍一、RAR自解压方式1、找到Unity打包的可执行程序2.创建自解压文件3.配置设置4、最后点击确定等待压缩完成即可&#xff08;默认生成位置为你选中文件右键点击添加到压缩文件时的路径&#xff09; 二、Setup Factory工具安装制作Window安装包相关常用…

助力城市部件[标石/电杆/光交箱/人井]精细化管理,基于YOLOv6开发构建生活场景下城市部件检测识别系统

井盖、店杆、光交箱、通信箱、标石等为城市中常见部件&#xff0c;在方便居民生活的同时&#xff0c;因为后期维护的不及时往往会出现一些“井盖吃人”、“线杆、电杆、线缆伤人”事件。造成这类问题的原因是客观的多方面的&#xff0c;这也是城市化进程不断发展进步的过程中难…

Vue - Class和Style绑定详解

1. 模板部分 <template><div><!-- Class 绑定示例 --><div :class"{ active: isActive, text-danger: hasError }">Hello, Vue!</div><!-- Class 绑定数组示例 --><div :class"[activeClass, errorClass]">Cla…