go mutex互斥锁使用Lock和Unlock方法占有释放资源
// Lock mutex 的锁方法。
func (m *Mutex) Lock() {
// 快速上锁.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// 快速上锁失败,将进行操作较多的上锁动作。
m.lockSlow()
}
func (m *Mutex) lockSlow() {
var waitStartTime int64 // 记录当前 goroutine 的等待时间
starving := false // 是否饥饿
awoke := false // 是否被唤醒
iter := 0 // 自旋次数
old := m.state // 当前 mutex 的状态
for {
// 当前 mutex 的状态已上锁,并且非饥饿模式,并且符合自旋条件
if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
// 当前还没设置过唤醒标识
if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
awoke = true
}
runtime_doSpin()
iter++
old = m.state
continue
}
new := old
// 如果不是饥饿状态,则尝试上锁
// 如果是饥饿状态,则不会上锁,因为当前的 goroutine 将会被阻塞并添加到等待唤起队列的队尾
if old&mutexStarving == 0 {
new |= mutexLocked
}
// 等待队列数量 + 1
if old&(mutexLocked|mutexStarving) != 0 {
new += 1 << mutexWaiterShift
}
// 如果 goroutine 之前是饥饿模式,则此次也设置为饥饿模式
if starving && old&mutexLocked != 0 {
new |= mutexStarving
}
//
if awoke {
// 如果状态不符合预期,则报错
if new&mutexWoken == 0 {
throw("sync: inconsistent mutex state")
}
// 新状态值需要清除唤醒标识,因为当前 goroutine 将会上锁或者再次 sleep
new &^= mutexWoken
}
// CAS 尝试性修改状态,修改成功则表示获取到锁资源
if atomic.CompareAndSwapInt32(&m.state, old, new) {
// 非饥饿模式,并且未获取过锁,则说明此次的获取锁是 ok 的,直接 return
if old&(mutexLocked|mutexStarving) == 0 {
break
}
// 根据等待时间计算 queueLifo
queueLifo := waitStartTime != 0
if waitStartTime == 0 {
waitStartTime = runtime_nanotime()
}
// 到这里,表示未能上锁成功
// queueLife = true, 将会把 goroutine 放到等待队列队头
// queueLife = false, 将会把 goroutine 放到等待队列队尾
runtime_SemacquireMutex(&m.sema, queueLifo, 1)
// 计算是否符合饥饿模式,即等待时间是否超过一定的时间
starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
old = m.state
// 上一次是饥饿模式
if old&mutexStarving != 0 {
if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
throw("sync: inconsistent mutex state")
}
delta := int32(mutexLocked - 1< // 此次不是饥饿模式又或者下次没有要唤起等待队列的 goroutine 了 if !starving || old>>mutexWaiterShift == 1 { delta -= mutexStarving } atomic.AddInt32(&m.state, delta) break } // 此处已不再是饥饿模式了,清除自旋次数,重新到 for 循环竞争锁。 awoke = true iter = 0 } else { old = m.state } } if race.Enabled { race.Acquire(unsafe.Pointer(m)) } }