当前位置: 首页 > news >正文

Go 源码剖析:sync.RWMutex 读写互斥锁原理

在并发场景中,很多业务具备读多写少的特征:大量协程并发读取共享资源,写操作相对稀少。 普通sync.Mutex是排他锁,不管读还是写,同一时间只允许一个协程持有锁,并发读场景性能很差。sync.RWMutex读写锁应运而生:读与读共享、读写互斥、写写互斥,大幅提升读密集场景并发能力。

本文基于 Go 标准库sync.RWMutex底层机制,拆解结构体、四大接口、锁竞争逻辑,同时解答经典问题:写锁如何避免饥饿。

一、RWMutex 基础特性

核心规则先行:

  1. ✅ 读锁不阻塞读锁:多个协程可以同时获取读锁并发读取
  2. ❌ 读锁阻塞写锁、写锁阻塞读锁
  3. ❌ 写锁阻塞写锁,同一时刻只能存在一个写协程

对外暴露 4 个核心 API:

  • RLock():获取读锁
  • RUnlock():释放读锁
  • Lock():获取写锁
  • Unlock():释放写锁

二、RWMutex 结构体核心字段

type RWMutex struct { w Mutex // 互斥锁,用于隔离多个写操作 writerSem uint32 // 写协程信号量,唤醒等待的写协程 readerSem uint32 // 读协程信号量,唤醒等待的读协程 readerCount int32 // 读计数器,标记当前读协程数量 readerWait int32 // 用于解决写锁饥饿问题 }

重点字段作用总览:

  1. w Mutex:底层互斥锁,保证同一时间只能有一个写协程
  2. readerCount:读者计数器,读写双方通信的标记
  3. readerWait:记录写锁到来之前,已经存在的读协程数量,防止写锁永久等待(写饥饿)

约定:

  • 无写操作:readerCount >= 0
  • 存在活跃写锁:readerCount < 0(写入时减去 1<<30)

三、四大接口底层执行流程

1. Lock () 获取写锁

  1. 先抢占内部互斥锁w,保证写写互斥,杜绝并发写;
  2. readerCount减去1 << 30,将计数器置为负数;

    这一步相当于打上标记:当前有写操作正在排队,后续新来的读锁全部阻塞;

  3. 将当前readerCount拷贝至readerWait,代表写锁到达前已存在的读协程数量
  4. 阻塞等待:直到所有正在执行的旧读协程全部释放锁,readerWait == 0,写协程正式执行业务。

关键点:写锁先标记、再等待。标记之后,新读请求直接阻塞,不会持续产生新读协程无休止抢占。

2. Unlock () 释放写锁

  1. 清除写标记:readerCount += 1 << 30
  2. 唤醒所有因为写锁阻塞的读协程;
  3. 释放内部互斥锁w,同时唤醒其他等待的写协程。

3. RLock () 获取读锁

  1. 原子操作readerCount++,增加读者计数;
  2. 判断readerCount是否为负数:
    • ≥0:无写操作,直接获取读锁成功;
    • <0:说明当前存在正在等待 / 执行的写协程,读协程阻塞等待信号量。

逻辑总结:只要写锁打上负数标记,新读请求全部排队。

4. RUnlock () 释放读锁

  1. 原子操作readerCount--,减少读者计数;
  2. 同时递减readerWait
  3. readerWait == 0:代表写锁到来之前所有旧读协程全部退出,唤醒等待中的写协程。

四、三大竞争关系原理拆解

4.1 写操作如何阻止其他写操作?

依靠结构体内置的Mutex w。 任何协程调用Lock()第一件事就是抢占该互斥锁。互斥锁天然排他,同一时间只会有一个协程拿到锁执行写逻辑,实现写写互斥。

4.2 写操作如何阻止读操作?

写锁执行时,执行readerCount -= 1 << 30,计数器变为负值。 后续所有协程调用RLock()时检测到readerCount < 0,判定存在写操作,主动阻塞。

注意:写锁标记生效之后新来的读协程会阻塞,但写锁到达前已经拿到读锁的协程可以继续执行,写锁需要等待它们全部释放。

4.3 读操作如何阻止写操作?

读协程执行RLock()会让readerCount ++。 写协程拿到内部互斥锁之后,需要等待readerWait归零,也就是等待所有已有读协程调用RUnlock()。只要还有活跃读协程,写协程持续阻塞。

五、核心难题:如何避免写锁饥饿?

什么是写饥饿?

如果没有readerWait机制:持续不断的读协程源源不断到来,readerCount永远大于 0,写协程会无限等待,永远无法获取锁。

readerWait 解决方案

  1. 写协程抢占内部互斥锁后,复制当前readerCountreaderWaitreaderWait=写锁到达这一刻,已经存在的读协程总数
  2. 只需要等待这一批 “旧读协程” 执行完毕;
  3. 每一次RUnlock()不仅递减readerCount,同时递减readerWait
  4. readerWait == 0,说明写锁到达前启动的读协程全部完成,立刻唤醒写协程。

这套机制的核心: 写锁到来之后新产生的读协程会被阻塞,不再新增任务,保证写协程一定能等到窗口期,从根源杜绝写饥饿。

六、使用注意事项(实践避坑)

  1. 锁配对RLock()必须搭配RUnlock()Lock()搭配Unlock(),禁止混用;
  2. 禁止递归加锁:同一个协程重复获取读锁 / 写锁会造成死锁;
  3. 适用场景:读多写少;读写频率接近时,RWMutex 额外的计数器、信号量开销可能高于普通 Mutex;
  4. 不要拷贝RWMutex:锁内部包含状态,拷贝会产生无效锁。

RWMutex设计十分巧妙: 依靠内置Mutex实现写写互斥;依靠readerCount作为标记协调读写冲突;依靠readerWait解决并发经典的写饥饿问题。 读懂计数器正负含义、return 前锁执行时序、信号量唤醒机制,就能彻底分清 Mutex 和 RWMutex 的选型场景。

http://www.jsqmd.com/news/1312481/

相关文章:

  • 2026年跨部门沟通工具横向对比:5款产品怎么选 - IM软件测评
  • 十三水相公规则说明:牌型判定逻辑与诀断十三水辅助解析
  • 2026年北京初创企业报税成本优化指南 - 万相科技
  • GEO优化服务哪家靠谱选型指南:深度测评与选型避坑清单 - 趣闻早乐评
  • 告别尬凑韵!《末字序本・十三辙》一站式解决所有押韵难题
  • 流式图表的增量渲染:百万点数据也要保持交互
  • 挑选抖音小店订单管理工具要看哪些标准,如何选出靠谱好用的辅助软件 - 抖掌柜
  • 2026 年现阶段,汾西靠谱的海洋动物展厂家推荐,别再花冤枉钱!这些海洋动物的冷知识,比现场看展还赚? - 行业甄选官
  • AI做PPT提示词怎么写,换个写法效果差很多
  • 如何将Android平板变成高效桌面?Smart Dock终极自定义指南
  • DIY高精度电能监测扩展板:从互感器选型到物联网集成的全流程解析
  • 抖店1688代发:手动拍单与自动拍单全维度拆解(效率、风险、综合成本) - 抖大侠
  • 2026年8月 北京非急救转运市场调研与合规服务商实地运营详解 - 平台推荐官
  • 5家GEO营销公司怎么选深度盘点:服务商实力大盘点与选型决策参考 - 趣闻早乐评
  • 2026年代理记账避坑指南:5个陷阱与3个选购标准 - 万相科技
  • Python零基础到实战:300集教程学习路径与就业技能拆解
  • 抖音小店零库存无货源运营模式优势明显吗,合规风险和售后难题怎么解决 - 抖掌柜
  • Electron桌宠开发指南:从窗口控制到动画交互完整实现
  • 5分钟终极指南:让Switch手柄在PC上完美运行
  • 2026年给养单元器材箱品牌甄选参考:高评价厂商综合评估与采购指南 - 优质品牌商家
  • C#进阶实战:异步编程、LINQ表达式树与依赖注入深度解析
  • 基于Qt+OpenCV+海康相机SDK的工业视觉检测上位机开发实战
  • 有实力的特变电工公司哪家好?2026年西南地区电线电缆供应商综合能力分析 - 优质品牌商家
  • STM32与ESP8266物联网开发:UART通信与AT指令实战指南
  • Python自动化图像处理与静态页面生成实战:构建数字艺术项目管理工具
  • 5秒获取百度网盘提取码:智能工具的极速解决方案
  • 2026年成都澳洲高尔夫定制旅行服务商口碑观察:诚信经营与专业能力解析 - 优质品牌商家
  • 【限时开源】我们刚交付的AI算力成本治理平台v2.3:自动识别低效Job、预测峰值负载、生成优化建议(仅开放72小时试用)
  • 抖音小店无货源模式订单履约数字化转型:替代手动下单的标准化运营模式 - 抖掌柜
  • 从传统百数表到数字化练习:一款数学启蒙 App 的设计思考