分享
  1. 首页
  2. 文章

谈谈自己对GO的RWMutex的理解

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

RWMutex核心还是基于Mutex的,如果想了解Mutex的话可以看一下我上一篇写的Mutex的文章

RWMutex的特性就是支持并发读。适用于读多写少的场景。

RWMutex的定义

type RWMutex struct {
 w Mutex // 互斥锁
 writerSem uint32 // 写锁用的信号量
 readerSem uint32 // 读锁用的信号量
 readerCount int32 // 当前正在执行读操作的goroutine数量
 readerWait int32 // 获取写锁时,当前还持有读锁的goroutine数量
}
const rwmutexMaxReaders = 1 << 30

RWMutex.Lock()

func (rw *RWMutex) Lock() {
 // 首先调用Mutex的Lock方法获取到锁
 rw.w.Lock()
 
 // 把readerCount改成负数,这样后续的读操作就会被阻塞
 // r 就是当前正在执行读操作的goroutine数量 
 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
 
 // 如果当前有正在执行读操作的goroutine
 // 把r赋值给readerWait
 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
 
 // 获取写锁的goroutine进入休眠,等待被唤醒
 runtime_SemacquireMutex(&rw.writerSem, false, 0)
 }
}

RWMutex.Unlock()

func (rw *RWMutex) Unlock() {
 // 把readerCount改成正数,这样后续读操作就不会被阻塞了
 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
 ...
 
 // 手动唤醒之前被写锁阻塞的读操作goroutine
 for i := 0; i < int(r); i++ {
 runtime_Semrelease(&rw.readerSem, false, 0)
 }
 
 // 释放互斥锁,其他写锁就可以竞争互斥锁了
 rw.w.Unlock()
}

RWMutex.RLock()

func (rw *RWMutex) RLock() {
 ...
 
 // readerCount + 1
 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
 
 // 小于0,说明有其他goroutine获取了写锁, 当前goroutine等待
 runtime_SemacquireMutex(&rw.readerSem, false, 0)
 }
 ...
}

RWMutex.RUnlock()

func (rw *RWMutex) RUnlock() {
 ...
 // readerCount - 1
 // readerCount < 0, 说明其他gouroutine获取了写锁,正在等待还持有读锁的goroutine释放读锁
 // readerCount >= 0, 说明没有写锁被阻塞,直接返回就行了
 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
 
 // 释放读锁
 rw.rUnlockSlow(r)
 }
 ...
}
func (rw *RWMutex) rUnlockSlow(r int32) {
 ...
 
 // readerWait - 1
 // 判断当前goroutine是不是最后一个释放读锁
 if atomic.AddInt32(&rw.readerWait, -1) == 0 {
 
 // 唤醒写锁
 runtime_Semrelease(&rw.writerSem, false, 1)
 }
}

总结

获取读锁的流程
  1. readerCount + 1
  2. 以readerCount<0,判断是否被写锁阻塞,是的话,当前goroutine进入休眠
释放读锁的流程
  1. readerCount - 1
  2. 以readerCount<0,判断是否有写锁
  3. 没有写锁的话,直接返回
  4. 有写锁的话,调用rUnlockSlow方法,readerWait - 1
  5. 如果readerWait == 0, 说明当前goroutine是写锁等待的最后一个读锁goroutine,需要唤醒写锁goroutine
获取写锁的流程
  1. 先获取互斥锁
  2. readerCount - rwmutexMaxReaders,后续读操作全部阻塞
  3. readerWait += readerCount,把当前正在执行读操作的数量加到readerWait上
  4. 如果readerWait != 0 ,说明当前还有其他goroutine持有读锁,当前goroutine进入睡眠,等待唤醒
释放写锁流程
  1. readerCount + rwmutexMaxReaders, 后续读锁不会阻塞
  2. readerCount代表之前被写锁阻塞的读锁goroutine个数,唤醒readerCount个读锁goroutine
  3. 最后释放互斥锁
最后

RWMutex相对Mutex,增加了读锁的控制,就代码逻辑复杂度而言,RWMutex比Mutex要简单很多,对Mutex的流程熟悉的话,很快就能掌握RWMutex的原理


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

本文来自:Segmentfault

感谢作者:iuoui

查看原文:谈谈自己对GO的RWMutex的理解

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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏