Golang 读写锁RWMutex 互斥锁Mutex 源码详解

Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现。

 

引子问题

我一般喜欢带着问题去看源码。那么对于读写锁,你是否有这样的问题,为什么可以有多个读锁?有没有可能出现有协程一直无法获取到写锁的情况?带着你的疑问来往下看看,具体这个锁是如何实现的。

如果你自己想看,我给出阅读的一个思路,可以先看读写锁,因为读写锁的实现依赖于互斥锁,并且读写锁比较简单一些,然后整理思路之后再去想一下实际的应用场景,然后再去看互斥锁。

下面我就会按照这个思路一步步往下走。

 

基础知识点

知识点1:信号量
信号量是 Edsger Dijkstra 发明的数据结构(没错就是那个最短路径算法那个牛人),在解决多种同步问题时很有用。其本质是一个整数,并关联两个操作:

申请acquire(也称为 wait、decrement 或 P 操作)
释放release(也称 signal、increment 或 V 操作)

acquire操作将信号量减 1,如果结果值为负则线程阻塞,且直到其他线程进行了信号量累加为正数才能恢复。如结果为正数,线程则继续执行。
release操作将信号量加 1,如存在被阻塞的线程,此时他们中的一个线程将解除阻塞。

知识点2:锁的定义

Golang 读写锁RWMutex 互斥锁Mutex 源码详解



在goalng中如果实现了Lock和Unlock方法,那么它就可以被称为锁。

知识点3:锁的自旋:(详见百度)

知识点4:cas算法:(最好有所了解,不知道问题也不大)

读写锁RWMutex 首先我们来看看RWMutex大体结构

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


看到结构发现读写锁内部包含了一个w Mutex互斥锁
注释也很明确,这个锁的目的就是控制多个写入操作的并发执行
writerSem是写入操作的信号量
readerSem是读操作的信号量
readerCount是当前读操作的个数
readerWait当前写入操作需要等待读操作解锁的个数
这几个现在看不懂没关系,后面等用到了你再回来看就好了。

 

然后我们看看方法

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


一共有5个方法,看起来就不复杂,我们一个个来看。

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


这个最简单,就是返回一个locker对象没啥好说的

问题的关键就在于锁和解锁的几个方法,因为我已经看过,所以推荐这几个方法的阅读顺序是RLock Lock RUnlock Unlock

 

RLock(获取读锁)

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


先不看竞态检测的部分,先重点看红色框中的部分
可以看到,其实很简单,每当有协程需要获取读锁的时候,就将readerCount + 1
但是需要注意的是,这里有一个条件,当readerCount + 1之后的值 < 0的时候,那么将会调用runtime_Semacquire方法

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


这个方法是一个runtime的方法,会一直等待传入的s出现>0的时候
然后我们可以记得,这里有这样一个情况,当出先readerCount + 1为负数的情况那么就会被等待,看注释我们可以猜到,是当有写入操作出现的时候,那么读操作就会被等待。

 

Lock(获取写锁)

Golang 读写锁RWMutex 互斥锁Mutex 源码详解


写锁稍微复杂一些,但是样子也差不多,我们还是先来看红色框中的部分。
首先操作最前面说的互斥锁,目的就是处理多个写锁并发的情况,因为我们知道写锁只有一把。这里不需要深入互斥锁,只需要知道,互斥锁只有一个人能拿到,所以写锁只有一个人能拿到。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgppd.html