이 글은 주로 Golang 뮤텍스 잠금의 상세한 내부 구현을 소개합니다. 편집자는 이것이 꽤 좋다고 생각합니다. 이제 이를 여러분과 공유하고 참고용으로 제공하겠습니다. 편집기를 따라 살펴보겠습니다. go 언어는 리소스를 공유하는 기본 방법인 mutex 잠금(sync.Mutex)을 제공합니다. sync.Mutex의 0 값은 잠겨 있지 않음을 의미합니다. 고루틴이 뮤텍스 잠금을 획득한 후 다른 고루틴은 고루틴이 뮤텍스 잠금을 해제할 때까지만 기다릴 수 있습니다. Mutex 구조에는 Lock 및 Unlock이라는 두 가지 기능만 있습니다. 이 문서에서는 그 사용법을 설명하지 않습니다.
sync.Mutex를 사용할 때는 절대 값을 복사하지 마세요. 잠금이 만료될 수 있기 때문입니다. IDE를 열고 sync.Mutex 코드로 점프하면 다음과 같은 구조를 갖고 있음을 알 수 있습니다:
type Mutex struct { state int32 //互斥锁上锁状态枚举值如下所示 sema uint32 //信号量,向处于Gwaitting的G发送信号 } const ( mutexLocked = 1 << iota // 1 互斥锁是锁定的 mutexWoken // 2 唤醒锁 mutexWaiterShift = iota // 2 统计阻塞在这个互斥锁上的goroutine数目需要移位的数值 )
Count 다음은 미리 설명해야 할 중요한 메서드 4개, 즉 Runtime_SemacquireMutex, Runtime_Semrelease,
1 런타임_canSpin의 소스 코드입니다. , golang의 스핀 잠금은 영원히 회전하지 않습니다. 런타임 패키지의 Runtime_canSpin 메서드에는 몇 가지 제한 사항이 있습니다. 전달된 iter는 4보다 크거나 같거나 CPU 코어 수가 1보다 작거나 같습니다. 1보다 크고 로컬 P 큐가 하나 이상 있고 로컬 P 큐가 실행될 수 있으며 G 큐는 비어 있습니다.//go:linkname sync_runtime_canSpin sync.runtime_canSpin func sync_runtime_canSpin(i int) bool { if i >= active_spin || ncpu <= 1 || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 { return false } if p := getg().m.p.ptr(); !runqempty(p) { return false } return true }
2.runtime_doSpin:
loop은 PAUSE 명령어를 호출합니다. PAUSE 명령어는 아무 작업도 수행하지 않지만 CPU 시간을 소비합니다. PAUSE 명령어를 실행할 때 CPU는 불필요한 최적화를 수행하지 않습니다.
//go:linkname sync_runtime_doSpin sync.runtime_doSpin func sync_runtime_doSpin() { procyield(active_spin_cnt) }
3.runtime_SemacquireMutex:
//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex func sync_runtime_SemacquireMutex(addr *uint32) { semacquire(addr, semaBlockProfile|semaMutexProfile) }
4.runtime_Semrelease:
//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease func sync_runtime_Semrelease(addr *uint32) { semrelease(addr) } Mutex的Lock函数定义如下 func (m *Mutex) Lock() { //先使用CAS尝试获取锁 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { //这里是-race不需要管它 if race.Enabled { race.Acquire(unsafe.Pointer(m)) } //成功获取返回 return } awoke := false //循环标记 iter := 0 //循环计数器 for { old := m.state //获取当前锁状态 new := old | mutexLocked //将当前状态最后一位指定1 if old&mutexLocked != 0 { //如果所以被占用 if runtime_canSpin(iter) { //检查是否可以进入自旋锁 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { //awoke标记为true awoke = true } //进入自旋状态 runtime_doSpin() iter++ continue } //没有获取到锁,当前G进入Gwaitting状态 new = old + 1<<mutexWaiterShift } if awoke { if new&mutexWoken == 0 { throw("sync: inconsistent mutex state") } //清除标记 new &^= mutexWoken } //更新状态 if atomic.CompareAndSwapInt32(&m.state, old, new) { if old&mutexLocked == 0 { break } // 锁请求失败,进入休眠状态,等待信号唤醒后重新开始循环 runtime_SemacquireMutex(&m.sema) awoke = true iter = 0 } } if race.Enabled { race.Acquire(unsafe.Pointer(m)) } } Mutex的Unlock函数定义如下 func (m *Mutex) Unlock() { if race.Enabled { _ = m.state race.Release(unsafe.Pointer(m)) } // 移除标记 new := atomic.AddInt32(&m.state, -mutexLocked) if (new+mutexLocked)&mutexLocked == 0 { throw("sync: unlock of unlocked mutex") } old := new for { //当休眠队列内的等待计数为0或者自旋状态计数器为0,退出 if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { return } // 减少等待次数,添加清除标记 new = (old - 1<<mutexWaiterShift) | mutexWoken if atomic.CompareAndSwapInt32(&m.state, old, new) { // 释放锁,发送释放信号 runtime_Semrelease(&m.sema) return } old = m.state } }
Mutex 잠금 충돌이 없는 경우가 가장 간단합니다. 갈등, 첫 번째 회전, 왜냐하면 대부분의 Mutex 보호 코드 세그먼트는 매우 짧으며 짧은 회전 후에 얻을 수 있습니다. 회전 대기가 실패하면 현재 고루틴은 세마포어를 통해 Gwaitting 상태로 들어가야 합니다.
위 내용은 Golang 뮤텍스 잠금 내부 구현 예에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!