go 源码解读 sync.RWMutex

news/2024/11/8 18:49:40/

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.ppmy.cn/news/1287166.html

相关文章

现代架构师需要的硬实力和软技能

架构师是现代软件开发领域中的重要职位之一&#xff0c;需要具备一定的硬实力和软技能&#xff0c;才能够在项目中发挥出最大的作用。 硬实力 1. 技术深度 架构师需要具备深厚的技术功底&#xff0c;熟练掌握多种编程语言和开发工具&#xff0c;了解各种开发框架和设计模式。…

golang第一卷---go入门

go入门 对于使用go的好处环境变量配置开发工具 参考网站 &#xff1a;go入门 对于使用go的好处 简单好记的关键词和语法。轻松上手&#xff0c;简单易学。更高的效率。比Java&#xff0c;C等拥有更高的编译速度&#xff0c;同时运行效率媲美C&#xff0c;同时开发效率非常高。…

linux基于用户身份对资源访问进行控制的解析及过程

linux中用户分为三类 1.超级用户&#xff08;root&#xff09; 拥有至高无上的权限 2.普通用户 人为创建、权限小&#xff0c;权限受到控制 3.程序用户 运行程序的用户&#xff0c;不是给人使用的&#xff0c;给程序使用的&#xff0c;一般不给登录&#xff01; 组账…

亚马逊云科技 re:Invent 大会 - ElastiCache Serverless 模式来袭

大会介绍 亚马逊云科技的 re:Invent 大会是一年一度的&#xff0c;面向全球技术开发者科技盛会。几乎每次都会发布云科技、云计算等相关领域的产品重磅更新&#xff0c;不但将时下主流热门的技术不断整合&#xff0c;也未将来的发展标明了方向。 亚马逊云科技开发者社区为开发…

C语言实现RSA算法加解密

使用c语言实现了RSA加解密算法&#xff0c;可以加解密文件和字符串。 rsa算法原理 选择两个大素数p和q&#xff1b;计算n p * q;计算φ(n)(p-1)(q-1)&#xff1b;选择与φ(n)互素的整数d&#xff1b;由de1 mod φ(n)计算得到e&#xff1b;公钥是(e, n), 私钥是(d, n);假设明…

【小沐学Python】Python实现免费天气预报获取(OpenWeatherMap)

文章目录 1、简介1.1 工具简介1.2 费用1.3 注册1.4 申请key 2、接口说明2.1 One Call 3.02.2 Current Weather and Forecasts collection2.2.1 API 调用2.2.2 API 参数 2.3 Historical Weather collection2.4 Weather Maps collection2.5 Other weather APIs 3、接口测试3.1 例…

Java EasyExcel 导入代码

Java EasyExcel 导入代码 导入方法 /*** 仓库库位导入** param req* param res* param files* throws Exception*/RequestMapping(value {"/import/line_store_locs"}, method {RequestMethod.POST})ResponseBodypublic void importStoreLoc(HttpServletRequest …

机器学习之BP神经网络精讲(Backpropagation Neural Network(附案例代码))

概念 BP神经网络(Backpropagation Neural Network)是一种常见的人工神经网络,它通过反向传播算法来训练网络,调整连接权重以最小化预测输出与实际输出之间的误差。这种网络结构包含输入层、隐藏层和输出层,使用梯度下降算法来优化权重。 结构: BP神经网络(Backpropag…