golang锁机制
Golang中如何避免死锁:加锁
- 读写锁中的可读锁(
sync.RWMutex
的RLock()
)可以嵌套使用的。 - 互斥锁(
sync.Mutex
和sync.RWMutex
的Lock()
)是不可以互相嵌套的,且不可以与可读锁嵌套。
之前我在读写锁和互斥锁上理解有偏差,认为读写锁与互斥锁是完全独立且相互对应的关系。现在理解为 互斥
只是一种特性。而把 sync.Mutex
叫作 全局锁, sync.RWMutex
叫作 读写锁。
全局锁 sync.Mutex
,是同一时刻某一资源只能上一个锁,此锁具有排他性,上锁后只能被此线程使用,直至解锁。加锁后即不能读也不能写。全局锁是互斥锁,即 sync.Mutex
是个互斥锁。
读写锁 sync.RWMutex
,将使用者分为读者和写者两个概念,支持同时多个读者一起读共享资源,但写时只能有一个,并且在写时不可以读。理论上来说,sync.RWMutex
的 Lock()
也是个互斥锁。
踩坑点
将上面的结论展开一下,更清晰得说(为避免理解偏差宁可唠叨一些):
sync.Mutex
的锁是不可以嵌套使用的。sync.RWMutex
的mu.Lock()
是不可以嵌套的。sync.RWMutex
的mu.Lock()
中不可以嵌套mu.RLock()
。(这是个注意的地方)
否则,会 panic fatal error: all goroutines are asleep - deadlock!
所以以下函数不会造成 panic:
var l sync.RWMutex func readAndRead() { // 可读锁内使用可读锁 l.RLock() defer l.RUnlock() l.RLock() defer l.RUnlock() } func main() { lockAndRead() time.Sleep(5 * time.Second) }
而将 readAndRead
换为以下三种函数均会造成 panic:
func lockAndLock() { // 全局锁内使用全局锁 l.Lock() defer l.Unlock() l.Lock() defer l.Unlock() } func lockAndRead() { // 全局锁内使用可读锁 l.Lock() defer l.Unlock() // 由于 defer 是栈式执行,所以这两个锁是嵌套结构 l.RLock() defer l.RUnlock() } func readAndLock() { // 可读锁内使用全局锁 l.RLock() defer l.RUnlock() l.Lock() defer l.Unlock() }
注: 在 goroutine 中的 panic 不会影响主程序,所以在测试时要注意并不是没有 panic 输出就一定是没发生。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix