百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

go语言并发原语RWMutex实现原理及闭坑指南

liebian365 2025-01-17 12:12 14 浏览 0 评论

1.RWMutex常用方法

  • Lock/Unlock
  • RLock/RUnlock
  • RLocker 为读操作返回一个Locker接 口的对象

2. RWMutex使用方法

 func main() {
 var counter Counter
 for i := 0; i < 10; i++ { // 10个reader
 go func() {
 for {
 counter.Count() // 计数器读操作
 time.Sleep(time.Millisecond)
 }
 }()
 }
 for { // 一个writer
 counter.Incr() // 计数器写操作
 time.Sleep(time.Second)
 }
 }
 // 一个线程安全的计数器
 type Counter struct {
 mu sync.RWMutex
 count uint64
 }
 // 使用写锁保护
 func (c *Counter) Incr() {
 c.mu.Lock()
 c.count++
 c.mu.Unlock()
 }
 / 使用读锁保护
 func (c *Counter) Count() uint64 {
 c.mu.RLock()
 defer c.mu.RUnlock()
 return c.count
 }

在读多写少的场景下,使用RWMutex性能要更好些

3.实现原理

RWMutex是基于Mutex实现的,以写优先来实现的

 type RWMutex struct {
     w Mutex // 互斥锁解决多个writer的竞争
     writerSem uint32 // writer信号量
     readerSem uint32 // reader信号量
     readerCount int32 // reader的数量
     readerWait int32 // writer等待完成的reader的数量(记录写锁来的时候前面还需要等待多少个读锁,其实也是在某一时刻 readerCount 的复制值)
 }
 const rwmutexMaxReaders = 1 << 30

说明:

  • 这里的常量 rwmutexMaxReaders,定义了最大的 reader 数量。
  • 字段 w:为 writer 的竞争锁而设计;
  • 字段 readerCount:记录当前 reader 的数量(以及是否有 writer 竞争锁);
  • readerWait:记录 writer 请求锁时需要等待 read 完成的 reader 的数量;
  • writerSem 和 readerSem:都是为了阻塞设计的信号量。

3.1 RLock/RUnlock的实现

 func (rw *RWMutex) RLock() {
     
      // 每次goroutine获得读锁,readerCount+1
     // 这里分两种情况:
     // 1. 当判断大于等于0, 证明当前没有写锁, 那么可以上读锁, 并且readerCount原子加1(读锁可重入[只要匹配了释放次数就行])
     // 2. 当判断小于0, 证明当前有写锁(Lock时会readerCount-rwmutexMaxReaders, 因此会小于0), 所以通过readerSem读信号量, 使读操作睡眠等待
 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
 // rw.readerCount是负值的时候,意味着此时有writer等待请求锁,因为writer优先
     runtime_SemacquireMutex(&rw.readerSem, false, 0)
     }
 }
 func (rw *RWMutex) RUnlock() {
     
     // 这里分两种情况:
     // 释放读锁, readerCount减1
     // 1.若readerCount大于0, 证明当前还有读锁, 直接结束本次操作
     // 2.若readerCount小于等于0, 证明已经没有读锁, 可以唤醒写锁(若有)
 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
     rw.rUnlockSlow(r) // 有等待的writer
     }
 }
 func (rw *RWMutex) rUnlockSlow(r int32) {
     // 1. 本来就没读锁, 调用RUnlock就发生panic
     // 2. 超过读锁的最大限制就发生panic
     if r+1 == 0 || r+1 == -rwmutexMaxReaders {
         race.Enable()
         throw("sync: RUnlock of unlocked RWMutex")
     }
     // readerWait--操作,如果readerWait--操作之后的值为0,说明,写锁之前,已经没有读锁了
     // 通过writerSem信号量,唤醒队列中第一个阻塞的写锁
 if atomic.AddInt32(&rw.readerWait, -1) == 0 {
 // 最后一个reader了,writer终于有机会获得锁了
 runtime_Semrelease(&rw.writerSem, false, 1)
 }

3.2 Lock的实现

 func (rw *RWMutex) Lock() {
 // 首先解决其他writer竞争问题
 rw.w.Lock()
  //// 先readerCount-rwmutexMaxReaders<0,标识有写锁来了,让后续来的读锁堵塞无法拿到锁
     // 再加回rwmutexMaxReaders得到当前读锁的个数
 // 反转readerCount,告诉reader有writer竞争锁
 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxRead
 // 如果当前有reader持有锁,那么需要等待
 //// 读锁个数不为0的时候,写锁阻塞,把当前的读锁个数readerCount赋值给readerWait
     // 用于标识写锁前面还需要等待多少个读锁
 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
 runtime_SemacquireMutex(&rw.writerSem, false, 0)
 }
 }

3.3 Unlock的实现

 func (rw *RWMutex) Unlock() {
 // 告诉reader没有活跃的writer了
 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
 // 唤醒阻塞的reader们
 for i := 0; i < int(r); i++ {
 runtime
 _
 Semrelease(&rw.readerSem, false, 0)
 }
 // 释放内部的互斥锁
 rw.w.Unlock()
 }

4 使用RWMutex的注意点

  • 不可复制
  • 重入导致死锁
  • 释放未加锁的RWMutex,会产生panic

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: