news 2026/10/30 15:48:21

Golang学习-sync.RWMutex 读写锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Golang学习-sync.RWMutex 读写锁

sync.RWMutex 读写锁


一、读写锁解决了什么问题

普通的 Mutex 不管操作是读还是写,一律互斥。但很多场景下,读操作远多于写操作,读操作之间本可以安全并发——它们不会修改数据。

RWMutex 提供两种锁:

  • 读锁(RLock):多个 goroutine 可以同时持有,彼此不阻塞
  • 写锁(Lock):独占,持有期间所有读锁和写锁都被阻塞

这一设计显著提升了读多写少场景的并发性能。

二、基本用法

var(mu sync.RWMutex balanceint64)// 读操作:使用读锁funcBalance()int64{mu.RLock()defermu.RUnlock()returnbalance}// 写操作:使用写锁(和 Mutex 一样)funcDeposit(amountint64){mu.Lock()defermu.Unlock()balance+=amount}

命名约定:Lock/Unlock 用于写,RLock/RUnlock 用于读。

三、锁的互斥规则

已有读锁已有写锁
请求读锁✅ 允许❌ 阻塞
请求写锁❌ 阻塞❌ 阻塞

核心理解:读读不互斥,其他都互斥。

四、适用场景判断

RWMutex 不是万能的。它的内部维护了更复杂的记账信息,无竞争时比 Mutex 稍慢。只有在以下条件同时满足时,RWMutex 才有优势:

  1. 读操作占绝大多数
  2. 存在锁竞争(多个 goroutine 同时在等锁)

如果没有竞争,Mutex 更轻更快;如果写操作频繁,RWMutex 不但无益,还会因为写锁不断阻塞读锁而导致性能下降。

五、读锁不能升级为写锁

这是一个经典的坑:你不能先 RLock,然后在持有读锁的情况下再 Lock。

mu.RLock()// ... 发现需要修改数据 ...mu.Lock()// 死锁!写锁在等待读锁释放,读锁永远不会释放

正确的做法是先 RUnlock,再 Lock,并且重新检查条件(因为释放读锁到获取写锁之间,数据可能已被其他 goroutine 改变):

mu.RLock()ifneedUpdate{mu.RUnlock()mu.Lock()// 重新检查!可能在等写锁期间别人已经改过了ifneedUpdate{/* 执行更新 */}mu.Unlock()}else{mu.RUnlock()}

六、不要假设"只读"就一定安全

即使一个方法看起来只读取数据,它也可能悄悄修改内部状态(比如更新访问计数器、填充缓存)。如果不确定,用 Mutex 更安全。

七、练习代码

保存为rwmutex_demo.go:

packagemainimport("fmt""sync""time")// 一个简单的配置中心:读多写少typeConfigstruct{mu sync.RWMutex datamap[string]string}funcNewConfig()*Config{return&Config{data:map[string]string{"version":"1.0","env":"dev"},}}// 读配置(允许并发读)func(c*Config)Get(keystring)string{c.mu.RLock()deferc.mu.RUnlock()returnc.data[key]}// 写配置(独占)func(c*Config)Set(key,valuestring){c.mu.Lock()deferc.mu.Unlock()c.data[key]=value}// 升级演示:先读后决定是否写func(c*Config)SetIfChanged(key,newValuestring)bool{c.mu.RLock()oldValue:=c.data[key]c.mu.RUnlock()ifoldValue==newValue{returnfalse// 值相同,不写}// 获取写锁,重新检查c.mu.Lock()deferc.mu.Unlock()// 重新检查:等写锁期间可能已被别人修改ifc.data[key]==newValue{returnfalse}c.data[key]=newValuereturntrue}funcmain(){cfg:=NewConfig()varwg sync.WaitGroup// 模拟 100 个并发读fori:=0;i<100;i++{wg.Add(1)gofunc(idint){deferwg.Done()v:=cfg.Get("version")_=v// 实际使用}(i)}// 模拟 5 个并发写fori:=0;i<5;i++{wg.Add(1)gofunc(idint){deferwg.Done()cfg.Set(fmt.Sprintf("key_%d",id),fmt.Sprintf("val_%d",id))}(i)}// 模拟升级锁模式fori:=0;i<10;i++{wg.Add(1)gofunc(idint){deferwg.Done()cfg.SetIfChanged("version","1.0")}(i)}wg.Wait()c.mu.RLock()fmt.Printf("配置条目数: %d\n",len(c.data))fmt.Printf("version: %s\n",c.data["version"])c.mu.RUnlock()}

运行结果:

配置条目数: 7 version: 1.0

八、关键要点

要点说明
RLock/RUnlock读锁,允许多个 goroutine 同时持有
Lock/Unlock写锁,独占,阻塞所有读写
不可升级不能从读锁升级为写锁,必须先释放再获取
重新检查释放读锁到获取写锁之间数据可能变化,需 double-check
适用场景读多写少 + 存在锁竞争
无竞争时Mutex 比 RWMutex 更轻更快
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 5:21:16

物理AI迎来“开悟时刻”:大晓发布开悟世界模型、以人为中心的环采方案2.0与三大行业解决方案

7月19日&#xff0c;世界人工智能大会主办&#xff0c;大晓机器人承办的2026 世界人工智能大会重磅议程 —— 世界模型 “六小龙” 巅峰论坛在沪启幕。本次论坛以驱动物理 AI 从 “理解” 到 “执行” 为核心命题&#xff0c;汇聚诺贝尔奖得主、全球顶尖学者、头部科技企业掌舵…

作者头像 李华
网站建设 2026/10/20 4:25:31

金额用 Long 还是 BigDecimal?

大家好&#xff0c;我是Java1234_小锋老师。做支付、电商、财务系统时&#xff0c;金额字段到底该怎么存&#xff1f;这篇文章用大白话讲清楚&#xff0c;并附上可直接参考的代码。先说结论场景推荐方案高并发交易、订单、余额Long&#xff08;以「分」为单位&#xff09;财务核…

作者头像 李华
网站建设 2026/10/20 4:02:25

grunt-contrib-copy 配置文件详解:从基础配置到高级选项

grunt-contrib-copy 配置文件详解&#xff1a;从基础配置到高级选项 【免费下载链接】grunt-contrib-copy Copy files and folders. 项目地址: https://gitcode.com/gh_mirrors/gr/grunt-contrib-copy grunt-contrib-copy 是 Grunt 生态中一款强大的文件复制工具&#x…

作者头像 李华
网站建设 2026/10/30 8:54:22

【限时解密】头部MCN私有数字人直播中台架构图:含ASR延迟<200ms的FPGA加速方案与商用授权清单

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI数字人24小时直播的商业价值与技术挑战 AI数字人24小时直播正成为电商、教育、金融与本地生活服务领域的新基建能力。相比真人主播&#xff0c;其核心优势在于可无限复制、零疲劳运行、多平台同步分发&#…

作者头像 李华
网站建设 2026/10/20 4:02:45

fluxsort基准测试:在不同数据集上的表现分析

fluxsort基准测试&#xff1a;在不同数据集上的表现分析 【免费下载链接】fluxsort A fast branchless stable quicksort / mergesort hybrid that is highly adaptive. 项目地址: https://gitcode.com/gh_mirrors/fl/fluxsort fluxsort是一款高性能的无分支稳定快速排序…

作者头像 李华