golang中的Mutex和RWMutex

Mutex互斥锁,对共享资源进行访问控制的主要手段,使用起来很方便,就Lock和Unlock(),使用简单不代表内部实现就简单,我们可以深入思考一下如果对Mutex重复解锁会出现什么情况?为什么会出现?

一:Mutex数据结构

src\sync\mutex.go

// A Mutex must not be copied after first use.
type Mutex struct {
	state int32
	sema  uint32
}

// A Locker represents an object that can be locked and unlocked.
type Locker interface {
	Lock()
	Unlock()
}

state:保存锁的相关数据 比如锁的状态、有多少个等待此锁的goroutine等等

sema:信号量,协程阻塞等待该信号量,解锁的协程释放信号量从而唤醒等待信号量的协程。

locked:表示当前锁是否被锁定,0没有锁定,1已经锁定

woken:是否有协程已被唤醒,0:没有协程唤醒 1:已有协程唤醒,正在加锁过程中

starving:表示当前锁是否饥饿,0没有饥饿,1饥饿状态 说明有协程阻塞时间超过了1ms

waiter:阻塞等待锁的协程个数,协程解锁时根据这个值来判断是否需要释放信号量

二:加解锁过程

1简单加锁:假设当前只有一个协程在加锁,没有其他协程干扰,locked会从0---->1,加锁过程会去判断locked标志位是否为0,如果是0则把locked置为1,代表加锁成功。

2加锁被阻塞:假设加锁时,锁已经被其他协程占用了(locked已经是1),此时waiter计数器会被+1,当前协程会被阻塞,直到locked变成0后才有机会被唤醒

3简单解锁:假设解锁时没有其他协程阻塞,locked从1---->0,而且不需要释放信号量

4解锁并唤醒协程:假设解锁时,有1个或多个协程阻塞,locked从1--->0,此时waiter>0,所以释放一个信号量,唤醒一个阻塞的协程,被唤醒的协程把Locked从0--->1,被唤醒的协程活得锁

三:自旋

加锁的时候,如果当前Locked已经是1,说明该锁已经被占用,尝试加锁的协程并不是马上转入阻塞,而是会持续的探测Locked是否变为0,这个过程就是自旋过程。

自旋的时间很短,如果在自旋的过程当中发现锁已经释放,那么这个协程就会立即获得所,此时即便有其他的协程被唤醒也无法获取锁,只能再次阻塞。

自旋的好处就是当加锁失败不必立即转入阻塞,有一定的机会获得锁,这样可以避免协程的切换。

自旋的优势:更充分的利用cpu,尽量避免协程切换。

自旋存在的问题:如果自旋过程中获得锁,之前被阻塞的协程就不会获得锁了,如果加锁的协程很多而且每次都通过自旋获得锁,那么之前被阻塞的进程就很难获得锁,就是处于一种饥饿状态。这就是前边的starving,这个状态下不会自旋。

四:Mutex模式

normal模式和starvation模式

normal:默认状态,该模式下,协程如果加锁失败不会立即转入阻塞排队,而是先判断是否满足自旋条件,如果满足会启动自旋过程尝试抢锁。

starvation:自旋过程中能抢到锁,一定意味着同一时刻有协程释放了锁,我们知道释放锁时如果发现有阻塞等待的协程,还会释放一个信号量来唤醒一个等待协程,被唤醒的协程得到CPU后开始运行,此时发现锁已被抢占了,自己只好再次阻塞,不过阻塞前会判断自上次阻塞到本次阻塞经过了多长时间,如果超过1ms的话,会将Mutex标记为”饥饿”模式,然后再阻塞。

处于饥饿模式下,不会启动自旋过程,也即一旦有协程释放了锁,那么一定会唤醒协程,被唤醒的协程将会成功获取锁,同时也会把等待计数减1。

五:woken状态

Woken状态用于加锁和解锁过程的通信,举个例子,同一时刻,两个协程一个在加锁,一个在解锁,在加锁的协程可能在自旋过程中,此时把Woken标记为1,用于通知解锁协程不必释放信号量了,好比在说:你只管解锁好了,不必释放信号量,我马上就拿到锁了。

重复解锁 为什么要panic

unlock的具体操作是 将locked置为0,然后判断waiter值,>0释放信号量

如果多次unlock,那么可能每次都释放了一个信号量,这样会唤醒多个协程,多个协程唤醒后都去抢锁,锁只有一把也就是只有一个协程会抢到,其他没有抢到的再次阻塞等待锁。这样会引起不必要的协程切换,浪费了资源。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值