当前位置: 首页 > news >正文

golang读写锁

1、介绍

在sync.Mutex基础上实现的读写锁,支持读读并发,其他还是串行,适用于读多写少的场景

2、结构体

type RWMutex struct {w           Mutex        // held if there are pending writerswriterSem   uint32       // semaphore for writers to wait for completing readersreaderSem   uint32       // semaphore for readers to wait for completing writersreaderCount atomic.Int32 // number of pending readersreaderWait  atomic.Int32 // number of departing readers
}
  • w:一个Mutex,仅用于解决多个writer之间的竞争问题,

  • writerSem:一个信号量,用于阻塞writer等待正在进行的reader完成。就是用于写操作加锁时候要等待所有的读操作完成,当所有读操作解锁结束后,会发送信号,然后写操作被唤醒

  • readerSem:一个信号量,用于阻塞reader等待正在进行的writer完成。读操作要获取锁时候,需要等待写操作解锁,解锁后向该信号量发送信号,解锁所有读操作

  • readerCount:记录当前正在进行的reader的数量,也用于表示是否有writer正在等待。

  • readerWait:记录writer请求锁时需要等待完成的reader的数量。待完成的读操作计数器,仅有写操作等待时候有用,代表在写操作前已经在执行的读操作数量

核心流程

3.1 读加锁

func (rw *RWMutex) RLock() {if race.Enabled {_ = rw.w.staterace.Disable()}if rw.readerCount.Add(1) < 0 {// A writer is pending, wait for it.runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}

原子操作 readCount 直接加1,如果结果不小于0,则说明加锁成功

如果小于0,则说明当前有writer正在等待或已经持有锁,则当前goroutine会阻塞在readerSem上,直到没有writer持有锁

注意: readCount小于0的原因是有写操作加锁时候会readCount-2的30次方,导致其值小于0

3.2 读解锁

func (rw *RWMutex) RUnlock() {if race.Enabled {_ = rw.w.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}if r := rw.readerCount.Add(-1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}if race.Enabled {race.Enable()}
}
func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()fatal("sync: RUnlock of unlocked RWMutex")}// A writer is pending.if rw.readerWait.Add(-1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}

原子操作 readCount 直接减1,如果结果不小于0,说明无写协程等待,直接解锁完成

如果小于0,说明有写协程正在等待,进入慢流程:

    原子操作把readerWait减1,若等于0 说明所有读操作都执行结束了,向writerSem发送信号,去唤醒等待的写协程。

  大于0,说明还有其他读操作,则不发送信号量直接返回,解锁结束

3.3 写加锁

保证写操作互斥,等待所有的读操作结束

func (rw *RWMutex) Lock() {if race.Enabled {_ = rw.w.staterace.Disable()}// First, resolve competition with other writers.rw.w.Lock()// Announce to readers there is a pending writer.r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.if r != 0 && rw.readerWait.Add(r) != 0 {runtime_SemacquireRWMutex(&rw.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}

先获取mutex的lock锁,确保同时只有一个写操作能进入后续

获取到互斥锁后,将readerCount设置为一个负数(通常是-readerCount-1),表示有writer正在等待锁。如果有正在进行的reader,writer会阻塞在writerSem上,直到所有reader都释放了锁。

r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders 

精妙之处在于,先把readCount变为负数(是的后面的再进来的读操作进入阻塞),然后又加回来赋值给临时变量r,走到后面判断,如果有读操作还在运行,把readWait设置为原readCount的值,如果大于0,则写操作进入阻塞太,登台所有读操作完成

if r != 0
  • 如果 r == 0,说明没有活跃读者,writer 可以直接获得锁,无需等待。
  • 如果 r > 0,说明有 r 个读者正在读,需要等他们退出。
b. rw.readerWait.Add(r) (如果有读操作在运行,则肯定大于0)
  • 将 readerWait 设置为 r(因为初始为 0,Add(r) 后就是 r);
  • readerWait 表示:“我这个 writer 需要等 r 个读者退出”。

⚠️ 注意:这里 Add(r) 的返回值是更新后的值。由于初始为 0,返回值就是 r,所以 != 0 恒成立(只要 r != 0)。

c. runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
  • 调用底层 runtime 函数,在 writerSem 信号量上阻塞当前 goroutine
  • 会一直 sleep,直到有读者在 RUnlock() 时发现 readerWait == 0,然后 runtime_Semrelease 唤醒它。

✅ 此时,writer 被挂起,等待所有“已进入”的读者退出。

3.4 写解锁

func (rw *RWMutex) Unlock() {if race.Enabled {_ = rw.w.staterace.Release(unsafe.Pointer(&rw.readerSem))race.Disable()}// Announce to readers there is no active writer.r := rw.readerCount.Add(rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()fatal("sync: Unlock of unlocked RWMutex")}// 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()if race.Enabled {race.Enable()}
}

释放写锁。将readerCount恢复为正数(通过加上一个常数,通常是rwmutexMaxReaders 2的30次方),表示writer已经释放了锁,此时如果有等待的reader或writer,它们可以根据情况被唤醒。

如果r大于0,代表有多个r还在等待,则通过runtime_Semrelease(&rw.readerSem, false, 0) 发送解锁信号量,唤醒读操作

等于0 则说明 没有读操作在等下,继续往下,

执行mutex.Unlock互斥锁解锁,写操作解锁完成,解锁过程中通知其他写操作被唤醒

http://www.dtcms.com/a/473665.html

相关文章:

  • 怎么用ftp清空网站大庆seo推广
  • 云南网官方网站博客园和wordpress
  • MyBatis基本工作原理
  • 第16届深圳国际移动电子展AI生活主题将带来哪些新体验?
  • AI智能体赋能战略分析与制订之仿真:“主权AI” —— 是国家安全的“诺亚方舟”,还是创新生态的“孤岛”?
  • 公司手机网站建设wordpress页眉页脚
  • MySQL时间格式转换,时间数据混乱不堪如何彻底重构?
  • Docker 安装 Node.js
  • vscode 怎么运行 c++ 文件
  • 【基础算法】记忆化搜索
  • wordpress yum上海搜索引擎优化公司排名
  • c++类和对象(下)
  • 算法7.0
  • 【异常处理——下】
  • axios请求
  • 109、23种设计模式之迭代器模式(18/23)
  • 餐饮设计公司网站wordpress如何保存
  • 前端页面出现问题ResizeObserver loop completed with undelivered notifications.
  • 有声阅读网站如何建设邵阳学院研究生与学科建设处网站
  • AWS RDS Aurora MySQL高CPU使用率问题诊断与解决实战
  • 【Swift】LeetCode 11. 盛最多水的容器
  • 设计模式之 享元模式 Flyweight
  • 智械觉醒当AI开始思考“我是谁”
  • 商河 网站建设公司网站的具体的建设方案
  • 湖南省网站备案婚纱摄影网站应该如何做优化
  • pytest学习
  • seo网站建设厦门百度广告代理商查询
  • 【全连接神经网络】基本原理
  • Go 异步编程
  • 基于贪心最小化包围盒策略的布阵算法