文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文掌握go的sync.RWMutex锁

2023-03-09 17:31

关注

在简略的说之前,首先要对RW锁的结构有一个大致的了解

type RWMutex struct {
    w           Mutex  // 写锁互斥锁,只锁写锁,和读锁无关
    writerSem   uint32 // sema锁--用于“写协程”排队等待
    readerSem   uint32 // sema锁--用于“读协程”排队等待
    readerCount int32  // 读锁的计数器
    readerWait  int32  // 等待读锁释放的数量
}

这里要额外说一句,writerSem和readerSem底层都是semaRoot,这个结构体有兴趣可以了解下,他的用法有点类似于一个简版的channel,很多地方把他的初始值设置为0,使得所有想获取该sema锁的协程都排队等待,也就是说初始值为0的sema锁,他本身起到的作用是成为一个协程等待队列,就像没有缓冲区的channel一样。

好现在进入正题。本文是为了在面试中能快速口述RW锁,并非为了完整解答RW锁的机制。

前提:

readerCount这个参数非常重要

读写锁互斥性

一个很重要的参数:const rwmutexMaxReaders = 1 << 30 ,rwmutexMaxReaders 非常大,意思是最多能有rwmutexMaxReaders(1 << 30  十进制为  4294967296)个协程同时持有读锁。

写锁上锁场景:

首先分析写锁,因为读锁的很多操作是根据写锁来的,如果一上来就说读锁,很多东西没法串起来

 func (rw *RWMutex) Lock() {
    // race.Enabled是官方的一些测试,性能检测的东西,无需关心,这个只在编译阶段才能启用
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	// 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)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
		race.Acquire(unsafe.Pointer(&rw.writerSem))
	}
}      

1.获取写锁--没有读锁等待

2.获取写锁--有读锁等待

3.获取写锁--前面已经有写锁了

后面的写协程也调用 rw.w.Lock() 进行加锁,因为前面有写锁已经获取了w,所以后续的写协程会因为获取不到w,而进入到w的sema队列里面,w是一个mutex的锁,mutex锁里是一个sema锁,sema锁因为没有设置初始值,所以退化为一个队列,而获取不到w锁的就会直接被阻塞在w的sema队列里,从而无法进行接下来的操作

写锁释放锁场景:

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

1.释放写锁--后面【没有】读锁等待

2.释放写锁--后面【有】读锁等待

3.释放写锁--后面有【写锁】等待

读锁上锁场景:

func (rw *RWMutex) RLock() {
    // race.Enabled都是测试用的代码,在阅读源码的时候可以跳过
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
    
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		// A writer is pending, wait for it.
		runtime_SemacquireMutex(&rw.readerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}

1.获取读锁--此时没有写锁.

最简单的场景,协程对rw.readerCount进行原子操作加一,如果得到的结果为正数,说明获取读锁成功。

2.获取读锁--前方已经有写锁抢占了该锁

3.获取读锁--前方有写锁抢已经被抢占,后方有写锁等待

读锁释放锁场景:

func (rw *RWMutex) RUnlock() {
	if race.Enabled {
		_ = rw.w.state
		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
		race.Disable()
	}
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		// Outlined slow-path to allow the fast-path to be inlined
		rw.rUnlockSlow(r)
	}
	if race.Enabled {
		race.Enable()
	}
}

1.释放读锁--后方没有写锁等待

2.释放读锁--后方有写锁等待

func (rw *RWMutex) rUnlockSlow(r int32) {
	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
		race.Enable()
		throw("sync: RUnlock of unlocked RWMutex")
	}
	// A writer is pending.
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		// The last reader unblocks the writer.
		runtime_Semrelease(&rw.writerSem, false, 1)
	}
}

这里是有个前提的,上面提到(详见上面的获取写锁的场景1),如果写协程进来想加写锁,需要把它需要等待的读锁数量从readerCount里赋值给readerWait。当它等待的读锁释放后,就需要用rUnlockSlow方法对readerWait进行减1,如果readWait == 0 ,说明这是最后一个需要等待的读锁也释放了,释放后就通知该写锁可以被唤醒了,锁给你了。

到此这篇关于go的sync.RWMutex锁的文章就介绍到这了,更多相关go的sync.RWMutex锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯