golang RWMutex读写锁分析

来源:互联网 发布:zxhn f420 有千兆端口 编辑:程序博客网 时间:2024/05/28 05:16

RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁

数据结构设计:

复制代码
type RWMutex struct {    w           Mutex  // 互斥锁    writerSem   uint32 // 写锁信号量    readerSem   uint32 // 读锁信号量    readerCount int32  // 读锁计数器    readerWait  int32  // 获取写锁时需要等待的读锁释放数量}
复制代码
复制代码
// 获取写锁func (rw *RWMutex) Lock() {    if race.Enabled {        _ = rw.w.state        race.Disable()    }    // 先获取一把互斥锁    rw.w.Lock()    // 减去最大的读锁数量,用0-负数来表示写锁已经被获取    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders    // 设置需要等待释放的读锁数量,如果有,则挂起获取读锁的goroutine    if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {        // 挂起,监控写锁信号量        runtime_Semacquire(&rw.writerSem)    }    if race.Enabled {        race.Enable()        race.Acquire(unsafe.Pointer(&rw.readerSem))        race.Acquire(unsafe.Pointer(&rw.writerSem))    }}
复制代码

按顺序这里应该介绍释放写锁的代码了,但是由于获取写锁中有很重要的几个逻辑变量,跟获取读锁时强依赖,所以在这里先说说获取读锁的逻辑

复制代码
// 获取读锁func (rw *RWMutex) RLock() {    if race.Enabled {        _ = rw.w.state        race.Disable()    }        // 每次获取读锁时,readerCount+1    // 如果写锁已经被获取,那么readerCount在-rwmutexMaxReaders与0之间,这时挂起获取读锁的goroutine,    // 如果写锁没有被获取,那么readerCount>=0,然后就没然后了    // 这样通过readerCount的正负就成了读锁与写锁互斥的判断条件    if atomic.AddInt32(&rw.readerCount, 1) < 0 {        // 挂起,监听readerSem信号量        runtime_Semacquire(&rw.readerSem)    }    if race.Enabled {        race.Enable()        race.Acquire(unsafe.Pointer(&rw.readerSem))    }}
复制代码
复制代码
// 释放读锁func (rw *RWMutex) RUnlock() {    if race.Enabled {        _ = rw.w.state        race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))        race.Disable()    }    // 读锁计数器-1    if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {        if r+1 == 0 || r+1 == -rwmutexMaxReaders {            race.Enable()            panic("sync: RUnlock of unlocked RWMutex")        }        // 如果获取写锁时的goroutine被阻塞,这时需要获取读锁的goroutine全部都释放,才会被唤醒        if atomic.AddInt32(&rw.readerWait, -1) == 0 { // 更新需要释放的读锁数量            // 更新信号量            runtime_Semrelease(&rw.writerSem)        }    }    if race.Enabled {        race.Enable()    }}
复制代码
复制代码
func (rw *RWMutex) Unlock() {    if race.Enabled {        _ = rw.w.state        race.Release(unsafe.Pointer(&rw.readerSem))        race.Release(unsafe.Pointer(&rw.writerSem))        race.Disable()    }    // 还原加锁时减去的那一部分readerCount    r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)    if r >= rwmutexMaxReaders {        race.Enable()        panic("sync: Unlock of unlocked RWMutex")    }    // 唤醒获取读锁期间所有被阻塞的goroutine    for i := 0; i < int(r); i++ {        runtime_Semrelease(&rw.readerSem)    }    // 释放互斥锁资源    rw.w.Unlock()    if race.Enabled {        race.Enable()    }}
复制代码

总结:

读写互斥锁的实现比较有技巧性一些,需要几点

1. 读锁不能阻塞读锁,引入readerCount实现

2. 读锁需要阻塞写锁,直到所以读锁都释放,引入readerSem实现

3. 写锁需要阻塞读锁,直到所以写锁都释放,引入wirterSem实现

4. 写锁需要阻塞写锁,引入Metux实现

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 感染hiv怎么办 肛门刺痛怎么办 小孩子便血怎么办 儿童便血怎么办 直肠息肉该怎么办 胆囊息肉病变怎么办 孩子得痔疮怎么办 学生长痔疮怎么办 学生有痔疮怎么办 久坐便血怎么办 结肠肠息肉怎么办 多发性肠息肉怎么办 直肠长息肉怎么办 小朋友肛门痒怎么办 肛门霉菌感染怎么办 肛门痔疮痒怎么办 胃食道逆流怎么办 小儿肛门瘙痒怎么办 宝宝屁眼发红怎么办 痔疮漏屎怎么办 肛门无缘无故疼怎么办 儿童肛门痒怎么办 牙缝开始变黑怎么办 严重龋齿口臭怎么办 拉肚子拉出痔疮怎么办 痔疮疼肚子疼怎么办 痔疮连着肚子疼怎么办 直肠癌肛门疼痛怎么办 患了直肠癌怎么办 婴儿屁眼烂怎么办 治疮拉不出来屎怎么办? 屁股眼里息肉怎么办 婴儿肛门畸形怎么办 肛门太紧老肛裂怎么办? 孕妇便秘做肛检怎么办 孩子不肯睡觉怎么办 外漏痔疮怎么办 痔疮出血疼怎么办 痔疮肛门疼怎么办 痔疮出肛门怎么办 外痔疮瘙痒怎么办