sync锁
一、Mutex
数据结构
type Mutex struct {
state int32 // 表示当前互斥锁的状态
sema uint32 // 用于控制锁状态的信号量
}
state
的基本信息:
-
Locked
互斥锁的锁定状态 -
Woken
表示从正常模式被从唤醒;当一个协程调用 Mutex.Lock() 时,如果 Mutex 已经被其他协程锁定,则该协程将被阻塞,并等待 Mutex 被释放。当其他协程调用 Mutex.Unlock() 释放 Mutex 时,如果等待的协程存在,则会设置 “woken” 标志,以通知等待的协程可以再次尝试获取 Mutex。这样,被阻塞的协程就可以继续执行下去,而不必一直等待下去。
-
Starving
当前的互斥锁进入饥饿状态; -
waitersCount
当前互斥锁上等待的 Goroutine 个数;
模式
- 普通模式
- 锁的等待者会按照先进先出的顺序获取锁。
- 刚被唤起的 Goroutine 与新创建的 Goroutine 竞争时,大概率会获取不到锁
- 饥饿模式
- 互斥锁会直接交给等待队列最前面的 Goroutine。
- 新的 Goroutine 在该状态下不能获取锁、也不会进入自旋状态,只会在队列的末尾等待。
- 如果一个 Goroutine 获得了互斥锁并且它在队列的末尾或者它等待的时间少于 1ms,那么当前的互斥锁就会切换回正常模式
加锁 Lock()
源码分析
func (m *Mutex) Lock() {
// 当锁的状态是 0 时,将 mutexLocked 位置成 1
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
return
}
// 不是 0 时 尝试通过自旋(Spinnig)等方式等待锁的释放
m.lockSlow()
}
- 自旋操作的流程
- 判断当前 Goroutine 能否进入自旋;
- 互斥锁只有在普通模式才能进入自旋;
- 运行在多 CPU 的机器上;
- 当前 Goroutine 为了获取该锁
- 判断当前 Goroutine 能否进入自旋;