Golang 的锁机制

Robin92 · · 5756 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

近日看了一篇 文章,讲到了用锁的 panic 问题,但并没有看懂,今日测试了许多场景,认为终于得出了结论:

  • 读写锁中的可读锁(sync.RWMutexRLock())可以嵌套使用的。
  • 互斥锁(sync.Mutexsync.RWMutexLock())是不可以互相嵌套的,且不可以与可读锁嵌套。

之前我在读写锁和互斥锁上理解有偏差,认为读写锁与互斥锁是完全独立且相互对应的关系。现在理解为 互斥 只是一种特性。而把 sync.Mutex 叫作 全局锁sync.RWMutex 叫作 读写锁

全局锁 sync.Mutex,是同一时刻某一资源只能上一个锁,此锁具有排他性,上锁后只能被此线程使用,直至解锁。加锁后即不能读也不能写。全局锁是互斥锁,即 sync.Mutex 是个互斥锁。

读写锁 sync.RWMutex ,将使用者分为读者和写者两个概念,支持同时多个读者一起读共享资源,但写时只能有一个,并且在写时不可以读。理论上来说,sync.RWMutexLock() 也是个互斥锁。

踩坑点

将上面的结论展开一下,更清晰得说(为避免理解偏差宁可唠叨一些):

  • sync.Mutex 的锁是不可以嵌套使用的。
  • sync.RWMutexmu.Lock() 是不可以嵌套的。
  • sync.RWMutexmu.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 输出就一定是没发生。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Robin92

查看原文:Golang 的锁机制

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

5756 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传