千家信息网

go RWMutex如何实现

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍"go RWMutex如何实现",在日常操作中,相信很多人在go RWMutex如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"go RWMute
千家信息网最后更新 2026年11月01日go RWMutex如何实现

这篇文章主要介绍"go RWMutex如何实现",在日常操作中,相信很多人在go RWMutex如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"go RWMutex如何实现"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Overview

go 里面的 rwlock 是 write preferred 的,可以避免写锁饥饿。

读锁和写锁按照先来后到的规则持有锁,一旦有协程持有了写锁,后面的协程只能在写锁被释放后才能得到读锁。

同样,一旦有 >= 1 个协程写到了读锁,只有等这些读锁全部释放后,后面的协程才能拿到写锁。

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}

Lock

func (rw *RWMutex) Lock() {        // First, resolve competition with other writers.        rw.w.Lock()        // Announce to readers there is a pending writer.        r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders        // Wait for active readers.        if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {                runtime_SemacquireMutex(&rw.writerSem, false, 0)        }}

Unlock

const rwmutexMaxReaders = 1 << 30func (rw *RWMutex) Unlock() {        // Announce to readers there is no active writer.        r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)        // Unblock blocked readers, if any.        for i := 0; i < int(r); i++ {                runtime_Semrelease(&rw.readerSem, false, 0)        }        // Allow other writers to proceed.        rw.w.Unlock()}

RLock

func (rw *RWMutex) RLock() {        if atomic.AddInt32(&rw.readerCount, 1) < 0 {                // A writer is pending, wait for it.                runtime_SemacquireMutex(&rw.readerSem, false, 0)        }}

RUnlock

func (rw *RWMutex) RUnlock() {        if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {                // Outlined slow-path to allow the fast-path to be inlined                rw.rUnlockSlow(r)        }}func (rw *RWMutex) rUnlockSlow(r int32) {        // A writer is pending.        if atomic.AddInt32(&rw.readerWait, -1) == 0 {                // The last reader unblocks the writer.                runtime_Semrelease(&rw.writerSem, false, 1)        }}

Q1: 多个协程并发拿读锁,如何保证这些读锁协程都不会被阻塞?

func (rw *RWMutex) RLock() {        if atomic.AddInt32(&rw.readerCount, 1) < 0 {                // A writer is pending, wait for it.                runtime_SemacquireMutex(&rw.readerSem, false, 0)        }}

拿读锁时,仅仅会增加 readerCount,因此读锁之间是可以正常并发的

Q2: 多个协程并发拿写锁,如何保证只会有一个协程拿到写锁?

func (rw *RWMutex) Lock() {        // First, resolve competition with other writers.        rw.w.Lock()        // Announce to readers there is a pending writer.        r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders        // Wait for active readers.        if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {                runtime_SemacquireMutex(&rw.writerSem, false, 0)        }}

拿写锁时,会获取 w.Lock,自然能保证同一时间只会有一把写锁

Q3: 在读锁被拿到的情况下,新协程拿写锁,如果保证写锁现成会被阻塞?

func (rw *RWMutex) Lock() {        // First, resolve competition with other writers.        rw.w.Lock()        // Announce to readers there is a pending writer.        r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders        // Wait for active readers.        if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {                runtime_SemacquireMutex(&rw.writerSem, false, 0)        }}

假设此时有 5 个协程拿到读锁,则 readerCount = 5,假设 rwmutexMaxReaders = 100。

此时有一个新的协程 w1 想要拿写锁。

在执行

r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders

后, rw.readerCount = -95,r = 5。

在执行

atomic.AddInt32(&rw.readerWait, r)

后,rw.readerWait = 5。

readerWait 记录了在获取写锁的这一瞬间有多少个协程持有读锁。这一瞬间之后,就算有新的协程尝试获取读锁,也只会增加 readerCount ,而不会动到 readerWait。

之后执行 runtime_SemacquireMutex() 睡在了 writerSem 这个信号量上面。

Q4: 在读锁被拿到的情况下,新协程拿写锁被阻塞,当旧有的读锁协程全部释放,如何唤醒等待的写锁协程

func (rw *RWMutex) RUnlock() {        if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {                // Outlined slow-path to allow the fast-path to be inlined                rw.rUnlockSlow(r)        }}func (rw *RWMutex) rUnlockSlow(r int32) {        // A writer is pending.        if atomic.AddInt32(&rw.readerWait, -1) == 0 {                // The last reader unblocks the writer.                runtime_Semrelease(&rw.writerSem, false, 1)        }}

继续上一步的场景,每当执行 RUnlock 时,readerCount 都会减去1。当 readerCount 为负数时,意味着有协程正在持有或者正在等待持有写锁。

之前的五个读协程中的四个,每次 RUnlock() 之后,readerCount = -95 - 4 = -99,readerWait = 5 - 4 = 1。

当最后一个读协程调用 RUnlock() 之后,readerCount 变成了 -100,readerWait 变成 0,此时会唤醒在 writerSem 上沉睡的协程 w1。

Q5: 在写锁被拿到的情况下,新协程拿读锁,如何让新协程被阻塞?

func (rw *RWMutex) RLock() {        if atomic.AddInt32(&rw.readerCount, 1) < 0 {                // A writer is pending, wait for it.                runtime_SemacquireMutex(&rw.readerSem, false, 0)        }}

继续上面的场景,readerCount = -100 + 1 = -99 < 0。

新的读协程 r1 被沉睡在 readerSem 下面。

假设此时再来一个读协程 r2,则 readerCount = -98,依旧沉睡。

Q6: 在写锁被拿到的情况下,新协程拿读锁,写锁协程释放,如何唤醒等待的读锁协程?

继续上面的场景,此时协程 w1 释放写锁

func (rw *RWMutex) Unlock() {        // Announce to readers there is no active writer.        r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)        // Unblock blocked readers, if any.        for i := 0; i < int(r); i++ {                runtime_Semrelease(&rw.readerSem, false, 0)        }        // Allow other writers to proceed.        rw.w.Unlock()}

在执行

atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)

后,r = readerCount = -98 + 100 = 2,代表此时有两个读协程 r1 和 r2 在等待

ps: 如果此时有一些新的协程想要拿读锁,他会因为 readerCount = 2 + 1 = 3 > 0 而顺利执行下去,不会被阻塞

之后 for 循环执行两次,将协程 r1 和 协程 r2 都唤醒了。

Q7: 在写锁被拿到的情况下,有两个协程分别去抢读锁和写锁,当写锁被释放时,这两个协程谁会胜利?

func (rw *RWMutex) Unlock() {        // Announce to readers there is no active writer.        r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)        // Unblock blocked readers, if any.        for i := 0; i < int(r); i++ {                runtime_Semrelease(&rw.readerSem, false, 0)        }        // Allow other writers to proceed.        rw.w.Unlock()}

由于是先唤醒读锁,再调用 w.Unlock() ,因此肯定是读协程先胜利!

认为写的比较巧妙的两个点

  • readerCount 与 rwmutexMaxReaders 的纠缠

    通过 readerCount + rwmutexMaxReaders 以及 readerCount - rwmutexMaxReaders 这两个操作可以得知当前是否有协程等待/持有写锁以及当前等待/持有读锁的协程数量

  • readerCount 与 readerWait 的纠缠

    在 Lock() 时直接将 readerCount 的值赋给 readerWait,在 readerWait = 0 而非 readerCount = 0 是唤醒写协程,可以避免在 Lock() 后来达到的读协程先于写协程被执行。

到此,关于"go RWMutex如何实现"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

两个 情况 阻塞 保证 学习 场景 多个 更多 正在 面的 在读 帮助 胜利 实用 巧妙 普通 先来后到 接下来 之间 代表 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司 南京欣网互联网络科技 美国互联网科技 艾特科技互联网 丰台科技园 互联网 深圳卡尔迅互联网科技有限公司怎么样 互联网教育科技公司vipkid 中国互联网有什么科技含量 互联网系的金融科技公司 互联网媒体平台算高科技产业 广州市恰铎互联网科技有限公司 亳州好赞互联网科技有限公司 国大互联网教育科技有限公司工作 允成互联网科技有限公司融资 佛山市拉法互联网科技有限公司 中天科技中国工业互联网 科技和互联网行业的特点 马化腾互联网科技教学 东风日产互联网科技公司叫什么 颐高云石互联网科技 互联网科技深圳 没上市的科技互联网公司 广州善班互联网科技 字节跳动和互联网科技 互联网公司科技股 重庆互联网医院沙龙医盟信息科技 哈尔滨屹滨互联网科技有限公司 富国互联网科技002692 西安有哪些互联网高科技企业 科技互联网企业在行动
0