Go RWMutex阅读
2021-08-25 | Tags: 源码
最近正好在读一些go的源码,有用到读写锁,看了一下标准实现,还是比较短的,就仔细看了看,看看go是怎么实现读写锁的
结构
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
}
总体来说比较简单writerSem,readerSem分别是读写唤醒的信号量,类似C++中的条件变量,主要为了最后一个读者或者写者Unlock时可以唤醒睡眠的进程
readerCount和readWait略微复杂一些,后面结合流程逻辑图解释一下
流程
读流程
写流程
分析
对于readerCount来说,只有在读上锁和读解锁时进行+1和-1,表示的是当前正在读的读者的数量。同时又在写上锁和写解锁时通过减去和加上一个大值(1<<30,并判断readerCount是否小于0)来表示是否有写者进入。通过这种合并操作可以省下一个单独的变量,并将readerCount通过atomic进行原子操作,提升了运行效率。
对于readerWait来说,表示的是在写者进入进行上锁后,需要等待的正在读的读者完成读并读解锁的数量,表现在写上锁时readerWait+=r(readerCount),当readerWait>0时,写者需要等待r个读者的最后一个读者读解锁并唤醒写者。要注意的是,对于无写者进入的状态(readerCount>0),在读解锁的时候将不会操作readerWait。
对于整个读写锁的实现而言,与传统的读者和写者的公平竞争读写权限不太一样,写者由更高的权限。表现在一个写者上锁进入后,将挂起后续所有新的读者上锁的请求,并等待当前正在读的读者数量r,当所有r都读完成并解锁后,一定会优先让写者进行写操作。者种实现可以避免写操作一直等待的情况。
总体来说Go RWMutex的实现还是比较精巧的,其中通过尽量使用atomic原子操作提升了整体的运行效率,但是同时也增加了代码的理解难度,中间的一些竞争流程还是需要仔细的推演才能理解其正确性。