欢迎来到 焦作市某某橡胶客服中心
全国咨询热线:020-123456789
联系我们

地址:联系地址联系地址联系地址

电话:020-123456789

传真:020-123456789

邮箱:admin@aa.com

新闻中心
golang-sync.RWMutex
  来源:焦作市某某橡胶客服中心  更新时间:2024-05-09 08:24:58

golang-sync.RWMutex

使用场景

sync.Mutex提供了互斥锁,可以保证在同一时间段内 ,有且仅有一个goroutine持有锁和操作共享资源 。其余goroutine只有在互斥锁被释放 ,成功获取到锁之后 ,才能操作共享资源

对共享资源的操作其实可以分为两种:

  • 读操作,不会改变共享资源
  • 写操作,会改变共享资源

在实际业务中,往往是读操作次数大于写操作次数,sync.Mutex提供的互斥锁 ,不能支持并发的读操作,所以就有了sync.RWMutex

sync.RWMutex有以下特点 :

  • 在同一时间段,可以有多个goroutine获取到读锁  ,即读共享
  • 在同一时间段 ,只能有一个goroutine获取到写锁 ,即写互斥
  • 在同一时间段,只能存在读锁或写锁  ,即读写互斥

如何使用

RWMutex结构如下:

type RWMutex struct { n w Mutex // held if there are pending writersn writerSem uint32 // semaphore for writers to wait for completing readersn readerSem uint32 // semaphore for readers to wait for completing writersn readerCount int32 // number of pending readersn readerWait int32 // number of departing readersn}nn// RWMutex提供了以下几个方法n// 加读锁nfunc (rw *RWMutex) RLock() { }n// 解读锁nfunc (rw *RWMutex) RUnlock() { }n// 尝试加读锁nfunc (rw *RWMutex) TryRLock() bool { }n// 加写锁nfunc (rw *RWMutex) Lock() { }n// 解写锁nfunc (rw *RWMutex) Unlock() { }n// 尝试加写锁nfunc (rw *RWMutex) TryLock() bool { }n// 返回一个Locker接口nfunc (rw *RWMutex) RLocker() Locker { }

使用RWMutex进行读写锁演示代码 :

func TestRWMutexLock(t *testing.T) { n var rw sync.RWMutexn var wg sync.WaitGroupn for i := 0; i < 5; i++ { n go func() { n wg.Add(1)n defer wg.Done()n // 读锁n rw.RLock()n defer rw.RUnlock()n time.Sleep(1 * time.Second)n fmt.Println("读操作")n }()n }nn for i := 0; i < 5; i++ { n go func() { n wg.Add(1)n defer wg.Done()n // 写锁n rw.Lock()n defer rw.Unlock()n time.Sleep(1 * time.Second)n fmt.Println("写操作")n }()n }n wg.Wait()n}

底层原理

字段含义

const rwmutexMaxReaders = 1 << 30nntype RWMutex struct { n w Mutex // held if there are pending writersn writerSem uint32 // semaphore for writers to wait for completing readersn readerSem uint32 // semaphore for readers to wait for completing writersn readerCount int32 // number of pending readersn readerWait int32 // number of departing readersn}

  • rwmutexMaxReaders:表示RWMutex能接受的最大读操作数量,超过最大数量就会panic
  • w:互斥锁 ,用于实现互斥写操作
  • writerSem :写操作信号量,用于写操作的阻塞和唤醒。当存在正在执行的读操作时,写操作会被阻塞;当读操作全部完成后 ,通过writerSem写操作信号量来唤醒写操作
  • readerSem:读操作信号量,用于读操作的阻塞和唤醒 。当存在正在执行的写操作时,读操作会被阻塞;当写操作完成后,通过readerSem读操作信号量唤醒读操作
  • readerCount:正在执行的读操作数量 ,当不存在写操作时 ,从0开始计数,通过正数来表示;当存在写操作时 ,从负的rwmutexMaxReaders开始计数 ,通过负数来表示
  • readerWait:写操作等待读操作的数量 ,当执行Lock方法时,如果当前存在正在执行的读操作,会将正在执行的读操作数量记录在readerWait中,并阻塞写操作;当读操作执行完成后,会更新readerWait;当readerWait为0时,会唤醒写操作
  • RWMutex具有写操作优先的特点 ,写操作发生时,只允许正在执行的读操作继续执行完成 ,后续新来的读操作都会被阻塞 ,直到写操作完成后进行唤醒

Lock

func (rw *RWMutex) Lock() { n if race.Enabled { n _ = rw.w.staten race.Disable()n }n // 加锁 ,保证写操作互斥n rw.w.Lock()n // 将readerCount更新为负值,表示当前有写操作n // 当readerCount为负数时 ,新的读操作会被阻塞n // r表示当前正在执行的读操作数量n r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReadersn // r != 0 表示当前存在正在执行的读操作n // 把当前正在执行的读操作数量更新到readerWait中n if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { n // 阻塞写操作,等待读操作执行完后唤醒n runtime_SemacquireMutex(&rw.writerSem, false, 0)n }n if race.Enabled { n race.Enable()n race.Acquire(unsafe.Pointer(&rw.readerSem))n race.Acquire(unsafe.Pointer(&rw.writerSem))n }n}

先通过Mutex进行加锁 ,保证写操作互斥

将readerCount更新为负值 ,表示当前有写操作。当readerCount为负数时,新的读操作会被阻塞

若当前存在正在执行的读操作,把当前正在执行的读操作数量更新到readerWait中

阻塞当前写操作 ,等待读操作执行完后唤醒

Unlock

func (rw *RWMutex) Unlock() { n if race.Enabled { n _ = rw.w.staten race.Release(unsafe.Pointer(&rw.readerSem))n race.Disable()n }n // 将readerCount更新为正数 ,表示当前没有写操作n r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)n if r >= rwmutexMaxReaders { n race.Enable()n throw("sync: Unlock of unlocked RWMutex")n }n // 唤醒所有等待的读操作n for i := 0; i < int(r); i++ { n runtime_Semrelease(&rw.readerSem, false, 0)n }n // 释放锁n rw.w.Unlock()n if race.Enabled { n race.Enable()n }n}

将readerCount更新为正数 ,表示当前没有写操作

若存在等待的读操作,则唤醒所有等待的读操作

释放互斥锁

RLock

func (rw *RWMutex) RLock() { n if race.Enabled { n _ = rw.w.staten race.Disable()n }n // 原子更新readerCount+1,表示读操作数量+1n // 若readerCount+1为负数,表示当前存在写操作,读操作会被阻塞 ,等待写操作完成后被唤醒n if atomic.AddInt32(&rw.readerCount, 1) < 0 { n runtime_SemacquireMutex(&rw.readerSem, false, 0)n }n if race.Enabled { n race.Enable()n race.Acquire(unsafe.Pointer(&rw.readerSem))n }n}

原子更新readerCount+1,读操作数量+1

如果readerCount+1为负数  ,则表示当前存在写操作,此时需要加锁的读操作会被阻塞 ,等待写操作完成后被唤醒

RUnlock

func (rw *RWMutex) RUnlock() { n if race.Enabled { n _ = rw.w.staten race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))n race.Disable()n }n // 原子更新readerCount-1,表示读操作数量-1n // 若readerCount-1为负数,表示当前读操作阻塞了写操作,需要进行额外处理n if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { n // Outlined slow-path to allow the fast-path to be inlinedn rw.rUnlockSlow(r)n }n if race.Enabled { n race.Enable()n }n}nnfunc (rw *RWMutex) rUnlockSlow(r int32) { n if r+1 == 0 || r+1 == -rwmutexMaxReaders { n race.Enable()n throw("sync: RUnlock of unlocked RWMutex")n }n // 原子更新readerWait-1  ,表示阻塞写操作的读操作数量-1n // 当readerWait-1为0时,表示导致写操作阻塞的所有读操作都已经执行完成 ,此时需要把阻塞的写操作唤醒n if atomic.AddInt32(&rw.readerWait, -1) == 0 { n runtime_Semrelease(&rw.writerSem, false, 1)n }n}

原子更新readerCount-1,表示读操作数量-1

若readerCount-1为负数 ,表示当前读操作阻塞了写操作 ,需要进行额外处理

原子更新readerWait-1,表示阻塞写操作的读操作数量-1

当readerWait-1为0时,表示导致写操作阻塞的所有读操作都已经执行完成  ,此时需要把阻塞的写操作唤醒


友情链接魔兽世界怀旧服史诗武器大全 全史诗武器效果介绍 怨灵之刃【必看】梦幻西游合宝宝攻略大揭秘!lol皮肤价格,LOL精选皮肤一览,总有一款让你欲罢不能!英雄联盟手游体验服怎么进 LOL手游体验服资格申请地址王冠 第二季 短评魔兽世界源生之能配方在哪里购买-源生之能配方购买位置一览魔羽角鹰兽成就攻略,魔兽71怎样获得失落已久的角鹰兽缰绳 在阿娜苏刷多少个水晶 搜狗【梵楚LT2451h】梵楚适用联想M7655DHF硒鼓粉盒打印机M7455DNF墨盒易加粉晒鼓套装鼓架Lenovo复印一体机LT2451激光专用碳粉盒【行情 报价 价格 评测】王者荣耀日之塔全新玩法上线 可多人组队通关DNF玲珑徽章怎么升级_DNF玲珑徽章升级材料大全[BLUE引擎]7月独家史诗巨作飞天传奇英雄合击三职业版本-稳定开放6年-集多种版本精华-BLUE《火炬之光2》火法怎么加点 火法加点攻略玩过4399吗?你最喜欢的一个游戏是什么?魔兽世界美味风蛇配方怎么获得,挂机魔兽披风怎么获得Agent A伪装游戏 安卓破解版v5.3.5适合苹果手机玩的游戏2022排行榜英雄合击1.95官方正版下载安装 v1.0.3幻塔密码锁全部答案以及位置是什么?幻塔密码锁密码大全2023最新攻略!大型社交游戏哪些好玩 五款热门社交手游下载推荐2023魔兽世界猎人宝宝推荐-九款猎人必带宝宝推荐奇迹之剑满v变态版【必看】梦幻西游合宝宝攻略大揭秘!魔兽世界其他种族坐骑声望好玩简单的手机游戏有哪些 2023简单操作的手机游戏盘点萌新想玩魔兽世界正式服,不知道哪个职业比较适合,还有种族,个人喜欢dps?十大免费变态手游盒子排行榜 好玩的变态游戏盒子下载大全解析付费社群的盈利点gm游戏合集魔兽世界战火梦魇兽能飞吗-揭开战火梦魇兽的飞行之谜魔兽世界怀旧服wlk冷酷复仇战士野外升级天赋怎么选(魔兽世界怀旧服wlk冷酷复仇战士野外升级天赋指南)幻化心得:玩家搭配心得 细节成为亮点!如果暴雪还你三个天赋或技能之猎人的绝唱交互设计方法论:从交互的角度剖析身边的产品王者荣耀QGfly教你玩关羽 飞牛关羽出装和铭文解析DNF魔道学者单刷全困难模式极限祭坛逐图攻略心得分享魔兽世界TBC:PVE常规消耗品盘点,想打第一要常备这7类消耗品奇迹暖暖春野密语答案大全 2023春野密语答案最新【教程】虐杀原形系列汉化&超清优化进阶终极方案(2024新版)英雄联盟s13阿卡丽出装铭文 英雄联盟s13阿卡丽怎么玩原创王者荣耀:西施FMVP皮肤清水芙融7.27上线;
联系我们

地址:联系地址联系地址联系地址

电话:020-123456789

传真:020-123456789

邮箱:admin@aa.com

0.2259

Copyright © 2024 Powered by 焦作市某某橡胶客服中心   sitemap