Heim >Backend-Entwicklung >Python-Tutorial >Detaillierte Erläuterung von Beispielen für die interne Implementierung der Golang-Mutex-Sperre

Detaillierte Erläuterung von Beispielen für die interne Implementierung der Golang-Mutex-Sperre

零下一度
零下一度Original
2017-07-02 11:07:001730Durchsuche

In diesem Artikel wird hauptsächlich die interne Implementierung der Golang-Mutex-Sperre ausführlich vorgestellt und erläutert. Der Herausgeber findet sie recht gut, daher werde ich sie jetzt mit Ihnen teilen und als Referenz verwenden. Folgen wir dem Editor, um einen Blick darauf zu werfen

Die Go-Sprache bietet eine sofort einsatzbereite Möglichkeit zum Teilen von Ressourcen, Mutex-Sperre (sync.Mutex), ein Nullwert von sync.Mutex bedeutet, dass eins vorhanden ist nicht gesperrt Ja, es kann direkt verwendet werden, nachdem eine Goroutine die Mutex-Sperre erhalten hat. Es gibt nur zwei Funktionen, die in der Mutex-Struktur verfügbar sind, nämlich Sperren und Entsperren Mutex-Sperre Es ist sehr einfach und dieser Artikel erklärt nicht seine Verwendung.

Kopieren Sie niemals Werte, wenn Sie sync.Mutex verwenden, da dies dazu führen kann, dass die Sperre abläuft. Wenn wir unsere IDE öffnen und zu unserem sync.Mutex-Code springen, werden wir feststellen, dass er die folgende Struktur hat:


type Mutex struct {
 state int32   //互斥锁上锁状态枚举值如下所示
 sema uint32  //信号量,向处于Gwaitting的G发送信号
}

const (
 mutexLocked = 1 << iota // 1 互斥锁是锁定的
 mutexWoken       // 2 唤醒锁
 mutexWaiterShift = iota // 2 统计阻塞在这个互斥锁上的goroutine数目需要移位的数值
)

Die oben genannten Statuswerte sind 0 (Verfügbar) 1 (gesperrt) 2~31 WartenWarteschlangeAnzahl

Das Folgende ist der Quellcode der Mutex-Sperre. Es gibt vier weitere wichtige Methoden, die im Voraus erklärt werden müssen. nämlich runtime_canSpin, runtime_doSpin, runtime_SemacquireMutex,

1. runtime_canSpin: Die Spin-Sperre in Golang führt nicht für immer aus. Der übergebene Iter ist größer oder gleich 4 oder die Anzahl der CPU-Kerne ist kleiner oder gleich 1, der maximale logische Prozessor ist größer als 1, es gibt mindestens eine lokale P-Warteschlange und die lokale P-Warteschlange kann das ausführen Die G-Warteschlange ist leer.


//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: ruft die Proyield-Funktion auf, die ebenfalls in Assembler implementiert ist. Innerhalb der Funktion ruft die -Schleife die PAUSE-Anweisung auf. Der PAUSE-Befehl bewirkt nichts, verbraucht aber CPU-Zeit. Bei der Ausführung des PAUSE-Befehls führt die CPU keine unnötigen Optimierungen durch.


//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 konfliktfrei ist der einfachste Fall. Wenn ein Konflikt vorliegt, wird zuerst Spin ausgeführt, da die meisten Codesegmente geschützt sind von Mutex sind sehr kurz und können nach einem kurzen Spin erhalten werden; wenn die Spin-Wartezeit fehlschlägt, muss die aktuelle Goroutine über ein Semaphor in den Gwaitting-Zustand wechseln.

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung von Beispielen für die interne Implementierung der Golang-Mutex-Sperre. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn