Maison  >  Article  >  développement back-end  >  Explication détaillée d'exemples d'implémentation interne du verrouillage mutex Golang

Explication détaillée d'exemples d'implémentation interne du verrouillage mutex Golang

零下一度
零下一度original
2017-07-02 11:07:001614parcourir

Cet article présente et explique principalement en détail l'implémentation interne du verrouillage mutex Golang. L'éditeur pense que c'est assez bon, je vais donc le partager avec vous maintenant et le donner comme référence. Suivons l'éditeur et jetons un coup d'œil

Le langage go fournit un moyen prêt à l'emploi de partager des ressources, un verrouillage mutex (sync.Mutex), une valeur nulle de sync.Mutex signifie que l'on est non verrouillé Oui, il peut être utilisé directement. Une fois qu'un goroutine a obtenu le verrou mutex, les autres goroutines ne peuvent qu'attendre que le goroutine libère le verrou mutex. Il n'y a que deux fonctions exposées dans la structure Mutex, à savoir Verrouiller et Déverrouiller. verrouillage mutex C'est très simple et cet article n'explique pas son utilisation.

Ne copiez jamais les valeurs lors de l'utilisation de sync.Mutex, car cela pourrait entraîner l'expiration du verrou. Lorsque nous ouvrons notre IDE et passons à notre code sync.Mutex, nous constaterons qu'il a la structure suivante :


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

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

Les valeurs d'état ci-dessus sont 0 ( Disponible) 1 (verrouillé) 2~31 En attenteQueueCount

Ce qui suit est le code source du verrouillage mutex. Il existe quatre autres méthodes importantes qui doivent être expliquées à l'avance, à savoir runtime_canSpin, runtime_doSpin, runtime_SemacquireMutex, runtime_Semrelease,

1. runtime_canSpin : le spin lock dans golang ne tournera pas éternellement. La méthode runtime_canSpin dans le package d'exécution applique certaines restrictions : l'iter transmis est supérieur ou égal à 4 ou le nombre de cœurs de processeur est inférieur ou égal à 1, le processeur logique maximum est supérieur à 1, il existe au moins une file d'attente P locale et la file d'attente P locale peut exécuter le La file d'attente G est vide.


//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 : appellera la fonction proyield, qui est également implémentée en langage assembleur. A l'intérieur de la fonction, la boucle appelle l'instruction PAUSE. L'instruction PAUSE ne fait rien, mais elle consomme du temps CPU Lors de l'exécution de l'instruction PAUSE, le CPU n'y effectuera pas d'optimisations inutiles.


//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 sans conflit est le cas le plus simple Lorsqu'il y a un conflit, la rotation est effectuée en premier, car la plupart des segments de code sont protégés. par Mutex sont très courts et peuvent être obtenus après une courte rotation ; si l'attente de rotation échoue, la Goroutine actuelle doit entrer dans l'état Gwaitting via un sémaphore.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn