zoukankan      html  css  js  c++  java
  • golang sync.RWMutex总结笔记

    背景

    最近项目中遇到两次RWMutex死锁问题,所以稍微看了一下资料和源码,稍作记录

    源码

    type RWMutex struct {
        w           Mutex  // held if there are pending writers
        writerSem   uint32 // semaphore for writers to wait for completing readers
        readerSem   uint32 // semaphore for readers to wait for completing writers
        readerCount int32  // number of pending readers
        readerWait  int32  // number of departing readers
    }

    RWMutex源码分析方面参考资料《go中的sync.RWMutex源码解读》写的比较详细和清晰易懂。

    总结

    go中锁都是不可重入的,所以同一个协程中获取两次RWmutex锁都可能出现死锁

    1. 同一协程获取两次RLock

      此场景参考前一篇文章《golang RWMutex RLock重入导致死锁》,同一协程两次获取RLock,如果在第二次获取RLock之前,有其他协程获取写锁Lock则会导致死锁。

      这种场景和同一协程先获取Lock再获取RLock是一样的原理,由于协程1获取了RLock,导致协程2获取Lock时会被阻塞,然而协程2获取Lock时会将rw.readerCount置为<0的负值,然后协程1再获取RLock也会被阻塞,所以导致两个协程相互阻塞了。

    2. 同一协程先获取RLock再获取Lock

    func TestLockUp(t *testing.T) {
    	var l sync.RWMutex
    	l.RLock()
    	t.Log("acquire read lock")
    	l.Lock()
    	t.Log("acquire write lock")
    	l.Unlock()
    	l.RUnlock()
    }
    

      读锁是会阻塞写锁的;从源代码中可以看到,在获取Lock时,如果已经有协程获取了RLock,则获取Lock的协程会阻塞在获取rw.writerSem信号量上,在读锁RLock解锁时会唤醒该信号量,然后RLock在等待Lock解锁才能执行RUnLock,因此造成死锁。

    3. 同一协程先获取Lock再获取RLock

    func TestLockDown(t *testing.T) {
    	var l sync.RWMutex
    	l.Lock()
    	t.Log("acquire write lock")
    	l.RLock()
    	t.Log("acquire read lock")
    	l.RUnlock()
    	l.Unlock()
    }
    

      写锁也会阻止读锁;从源代码中可以看到,当一个协程获取写锁Lock时,会将rw.readerCount置为<0的负值,而当获取读锁RLock时,先对rw.readerCount加1,如果加1后的结果为负值,则表示有协程已经获取到写锁或者正在等待获取写锁,因而该获取读锁的协程会阻塞在获取rw.readerSem信号量上;因此同一个协程先后获取Lock和RLock会相互阻塞等待从而造成死锁。

      从这里也能看出来,在RWMutex中,写锁的优先级高于读锁,只要有协程在等待获取写锁,后续的读锁都需要等待。

    4. 同一协程获取两次Lock

    func TestReLock(t *testing.T) {
    	var l sync.RWMutex
    	l.Lock()
    	t.Log("acquire write lock")
    	l.Lock()
    	t.Log("acquire write lock")
    	l.Unlock()
    	l.Unlock()
    }
    

      写锁阻止写锁;同样,第二个Lock会阻塞在获取rw.writeSem信号量上,导致两个Lock都无法执行Unlock。

      总之一句话,go中RWLock不支持可重入,不要在同一协程调用多次RWMutex的锁,不管读锁还是写锁。

    5. 写锁优先级高于读锁

      有写锁等待时,优先加写锁,因此写锁不至于饿死一直无法获取到锁。

      写操作到来时,会把RWMutex.readerCount值拷贝到RWMutex.readerWait中,用于标记排在写操作前面的读者个数。

      前面的读操作结束后,除了会递减RWMutex.readerCount,还会递减RWMutex.readerWait值,当RWMutex.readerWait值变为0时唤醒写操作。

    参考资料

    go中的sync.RWMutex源码解读

    Go 并发实战 -- sync RWMutex

  • 相关阅读:
    系统综合实践 第1次实践作业
    Linq 中按照多个值进行分组(GroupBy,Count)
    敏捷开发综述
    心率
    二维数组 子数组和的最大值
    电梯调度算法
    课堂测试用例。。。
    分析文本文件中各单词出现的频率,并把频率最高的十个词打印出来
    dwz tree组件 取得所选择的值
    SQL Server -- 已成功与服务器建立连接,但是在登录过程中发生错误
  • 原文地址:https://www.cnblogs.com/luoming1224/p/14666173.html
Copyright © 2011-2022 走看看