Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现。
引子问题
我一般喜欢带着问题去看源码。那么对于读写锁,你是否有这样的问题,为什么可以有多个读锁?有没有可能出现有协程一直无法获取到写锁的情况?带着你的疑问来往下看看,具体这个锁是如何实现的。
如果你自己想看,我给出阅读的一个思路,可以先看读写锁,因为读写锁的实现依赖于互斥锁,并且读写锁比较简单一些,然后整理思路之后再去想一下实际的应用场景,然后再去看互斥锁。
下面我就会按照这个思路一步步往下走。
基础知识点
知识点1:信号量
信号量是 Edsger Dijkstra 发明的数据结构(没错就是那个最短路径算法那个牛人),在解决多种同步问题时很有用。其本质是一个整数,并关联两个操作:
申请acquire(也称为 wait、decrement 或 P 操作)
释放release(也称 signal、increment 或 V 操作)
acquire操作将信号量减 1,如果结果值为负则线程阻塞,且直到其他线程进行了信号量累加为正数才能恢复。如结果为正数,线程则继续执行。
release操作将信号量加 1,如存在被阻塞的线程,此时他们中的一个线程将解除阻塞。
知识点2:锁的定义
在goalng中如果实现了Lock和Unlock方法,那么它就可以被称为锁。
知识点3:锁的自旋:(详见百度)
知识点4:cas算法:(最好有所了解,不知道问题也不大)
读写锁RWMutex 首先我们来看看RWMutex大体结构
看到结构发现读写锁内部包含了一个w Mutex互斥锁
注释也很明确,这个锁的目的就是控制多个写入操作的并发执行
writerSem是写入操作的信号量
readerSem是读操作的信号量
readerCount是当前读操作的个数
readerWait当前写入操作需要等待读操作解锁的个数
这几个现在看不懂没关系,后面等用到了你再回来看就好了。
然后我们看看方法 
一共有5个方法,看起来就不复杂,我们一个个来看。 
这个最简单,就是返回一个locker对象没啥好说的
问题的关键就在于锁和解锁的几个方法,因为我已经看过,所以推荐这几个方法的阅读顺序是RLock Lock RUnlock Unlock
RLock(获取读锁) 
先不看竞态检测的部分,先重点看红色框中的部分
可以看到,其实很简单,每当有协程需要获取读锁的时候,就将readerCount + 1
但是需要注意的是,这里有一个条件,当readerCount + 1之后的值 < 0的时候,那么将会调用runtime_Semacquire方法

这个方法是一个runtime的方法,会一直等待传入的s出现>0的时候
然后我们可以记得,这里有这样一个情况,当出先readerCount + 1为负数的情况那么就会被等待,看注释我们可以猜到,是当有写入操作出现的时候,那么读操作就会被等待。
Lock(获取写锁) 
写锁稍微复杂一些,但是样子也差不多,我们还是先来看红色框中的部分。
首先操作最前面说的互斥锁,目的就是处理多个写锁并发的情况,因为我们知道写锁只有一把。这里不需要深入互斥锁,只需要知道,互斥锁只有一个人能拿到,所以写锁只有一个人能拿到。