>  기사  >  백엔드 개발  >  Golang 뮤텍스 잠금 내부 구현 예에 대한 자세한 설명

Golang 뮤텍스 잠금 내부 구현 예에 대한 자세한 설명

零下一度
零下一度원래의
2017-07-02 11:07:001611검색

이 글은 주로 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数目需要移位的数值
)

위 상태 값은 0(사용 가능) 1(잠김) 2~31 대기 중

Queue

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:

어셈블리 언어로도 구현되는 proyield 함수를 호출합니다. 함수 내부의

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.