欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你了解Go語(yǔ)言中鎖的實(shí)現(xiàn)

 更新時(shí)間:2023年03月15日 09:59:55   作者:firaga  
這篇文章主要帶大家一起學(xué)習(xí)一下go鎖和讀寫鎖的總結(jié)文檔,?主要從"參考"部分的文章結(jié)合源碼學(xué)習(xí),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

此文為學(xué)習(xí)go鎖和讀寫鎖的總結(jié)文檔, 主要從"參考"部分的文章結(jié)合源碼學(xué)習(xí), 總結(jié)于此.

ps: 注釋"r: "開頭代表來自參考文章, 見最后

Mutex

省流不看版:

沒鎖直接鎖,鎖不上自旋或讓出調(diào)度等待喚醒,直到鎖上.

饑餓模式阻塞隊(duì)列先進(jìn)先出

Lock

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
  // Fast path: grab unlocked mutex.
  // 上鎖,成功返回
  if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
    if race.Enabled {
      race.Acquire(unsafe.Pointer(m))
    }
    return
  }
  // Slow path (outlined so that the fast path can be inlined)
  //已經(jīng)鎖上的寫成進(jìn)入慢鎖流程
  m.lockSlow()
}

lockSlow

func (m *Mutex) lockSlow() {
  var waitStartTime int64 //執(zhí)行時(shí)間
  starving := false //當(dāng)前請(qǐng)求是否是饑餓模式
  awoke := false //當(dāng)前請(qǐng)求是否是喚醒狀態(tài)
  iter := 0 //自旋次數(shù)
  old := m.state //舊state值
  for {
    // Don't spin in starvation mode, ownership is handed off to waiters
    // so we won't be able to acquire the mutex anyway.
    //舊state值已上鎖,并且未進(jìn)入饑餓模式,且可以自旋,進(jìn)入自旋邏輯
    if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
      // Active spinning makes sense. 
      // Try to set mutexWoken flag to inform Unlock
      // to not wake other blocked goroutines.
      // 當(dāng)前協(xié)程未喚醒 
      //&& old.state 為未喚起狀態(tài),就是說沒有其他被喚起的waiter
      //&& waiter數(shù)>0 
      //&& m.state標(biāo)記為喚起狀態(tài)成功
      if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
        atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
        //標(biāo)記當(dāng)前協(xié)程為喚起狀態(tài)
        //r: 這是為了通知在解鎖Unlock()中不要再喚醒其他的waiter了
        awoke = true
      }
      //自旋
      runtime_doSpin()
      //自旋計(jì)數(shù)器
      iter++
      old = m.state
      continue
    }
    //r: old是鎖當(dāng)前的狀態(tài),new是期望的狀態(tài),以期于在后面的CAS操作中更改鎖的狀態(tài)
    //new代表期望的state值
    new := old
    // Don't try to acquire starving mutex, new arriving goroutines must queue.
    //old不是饑餓狀態(tài),new帶上上鎖標(biāo)志位,也就是饑餓狀態(tài)不上鎖
    if old&mutexStarving == 0 {
      new |= mutexLocked
    }
    //舊state值是上鎖狀態(tài)或饑餓狀態(tài),新state waiter數(shù)+1
    //r: 表示當(dāng)前goroutine將被作為waiter置于等待隊(duì)列隊(duì)尾
    if old&(mutexLocked|mutexStarving) != 0 {
      new += 1 << mutexWaiterShift
    }
    // The current goroutine switches mutex to starvation mode.
    // But if the mutex is currently unlocked, don't do the switch.
    // Unlock expects that starving mutex has waiters, which will not
    // be true in this case.
    //當(dāng)前協(xié)程為饑餓狀態(tài)&&舊state已上鎖,新state加饑餓標(biāo)志位
    if starving && old&mutexLocked != 0 {
      new |= mutexStarving
    }
    //r:? 當(dāng)awoke為true,則表明當(dāng)前goroutine在自旋邏輯中,成功修改鎖的Woken狀態(tài)位為1
    if awoke {
      // The goroutine has been woken from sleep,
      // so we need to reset the flag in either case.
      if new&mutexWoken == 0 {
        throw("sync: inconsistent mutex state")
      }
      //新state關(guān)閉喚醒標(biāo)志位
      //r: 因?yàn)樵诤罄m(xù)的邏輯中,當(dāng)前goroutine要么是拿到鎖了,要么是被掛起。
      // 如果是掛起狀態(tài),那就需要等待其他釋放鎖的goroutine來喚醒。
      // 假如其他goroutine在unlock的時(shí)候發(fā)現(xiàn)Woken的位置不是0,則就不會(huì)去喚醒,那該goroutine就無(wú)法再醒來加鎖。(見unlock邏輯)
?
      new &^= mutexWoken
    }
    //r: 嘗試將鎖的狀態(tài)更新為期望狀態(tài)
    if atomic.CompareAndSwapInt32(&m.state, old, new) {
      //舊state不是鎖或饑餓狀態(tài),上鎖成功,返回
      if old&(mutexLocked|mutexStarving) == 0 {
        break // locked the mutex with CAS
      }
      // If we were already waiting before, queue at the front of the queue.
      //r: 如果走到這里,那就證明當(dāng)前goroutine沒有獲取到鎖
      // 這里判斷waitStartTime != 0就證明當(dāng)前goroutine之前已經(jīng)等待過了,則需要將其放置在等待隊(duì)列隊(duì)頭
      //進(jìn)入隊(duì)列是否排在最前
      queueLifo := waitStartTime != 0
      if waitStartTime == 0 {
        waitStartTime = runtime_nanotime()
      }
      //阻塞
      runtime_SemacquireMutex(&m.sema, queueLifo, 1)
      //r: 被信號(hào)量喚醒之后檢查當(dāng)前goroutine是否應(yīng)該表示為饑餓
      // (這里表示為饑餓之后,會(huì)在下一輪循環(huán)中嘗試將鎖的狀態(tài)更改為饑餓模式)
      // 1. 如果當(dāng)前goroutine已經(jīng)饑餓(在上一次循環(huán)中更改了starving為true)
      // 2. 如果當(dāng)前goroutine已經(jīng)等待了1ms以上
      
      //被信號(hào)量喚醒后當(dāng)前協(xié)程是否進(jìn)入饑餓狀態(tài)
      //1. 之前是饑餓狀態(tài)
      //2. 運(yùn)行時(shí)間超過1ms
      starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
      // 再次獲取鎖狀態(tài)
      old = m.state
      if old&mutexStarving != 0 {
        // If this goroutine was woken and mutex is in starvation mode,
        // ownership was handed off to us but mutex is in somewhat
        // inconsistent state: mutexLocked is not set and we are still
        // accounted as waiter. Fix that.
        //饑餓模式協(xié)程是在Unlock()時(shí)handoff到當(dāng)前協(xié)程的
        
        //r:? 如果當(dāng)前鎖既不是被獲取也不是被喚醒狀態(tài),或者等待隊(duì)列為空
        // 這代表鎖狀態(tài)產(chǎn)生了不一致的問題
        if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
          throw("sync: inconsistent mutex state")
        }
        //m.state 上鎖,waiter數(shù)-1
        delta := int32(mutexLocked - 1<<mutexWaiterShift)
        //當(dāng)前協(xié)程不是饑餓狀態(tài)或舊state的waiter數(shù)=1,則m.state饑餓標(biāo)志位置0
        if !starving || old>>mutexWaiterShift == 1 {
          // Exit starvation mode.
          // Critical to do it here and consider wait time.
          // Starvation mode is so inefficient, that two goroutines
          // can go lock-step infinitely once they switch mutex
          // to starvation mode.
          delta -= mutexStarving
        }
        atomic.AddInt32(&m.state, delta)
        //拿到鎖,退出.
        break
      }
      awoke = true
      iter = 0
    } else {
      //執(zhí)行循環(huán)前的語(yǔ)句,恢復(fù)最新現(xiàn)場(chǎng)
      old = m.state
    }
  }
?
  if race.Enabled {
    race.Acquire(unsafe.Pointer(m))
  }
}

Unlock

// Unlock unlocks m.
// It is a run-time error if m is not locked on entry to Unlock.
//
// A locked Mutex is not associated with a particular goroutine.
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
  if race.Enabled {
    _ = m.state
    race.Release(unsafe.Pointer(m))
  }
?
  // Fast path: drop lock bit.
  //m.state取消鎖狀態(tài),返回值new代表修改后的新值
  //如果為0代表沒有其他鎖了,退出;否則進(jìn)入unlockSlow()
  //鎖空閑有兩種情況:
  //1. 所有位為0,代表沒有鎖了
  //2. 標(biāo)志位為0, waiter數(shù)量>0,還有協(xié)程在等待解鎖
  new := atomic.AddInt32(&m.state, -mutexLocked)
  if new != 0 {
    // Outlined slow path to allow inlining the fast path.
    // To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.
    m.unlockSlow(new)
  }
}

UnlockSlow

func (m *Mutex) unlockSlow(new int32) {
  if (new+mutexLocked)&mutexLocked == 0 {
    throw("sync: unlock of unlocked mutex")
  }
  if new&mutexStarving == 0 {
    old := new
    for {
      // If there are no waiters or a goroutine has already
      // been woken or grabbed the lock, no need to wake anyone.
      // In starvation mode ownership is directly handed off from unlocking
      // goroutine to the next waiter. We are not part of this chain,
      // since we did not observe mutexStarving when we unlocked the mutex above.
      // So get off the way.
      //解鎖,結(jié)束,退出
      //1. 沒有waiter了
      //2. 已上鎖
      //3. 鎖處于喚醒狀態(tài),表示有協(xié)程被喚醒
      //4. 饑餓模式, 所有權(quán)交給了被解鎖饑餓模式的waiter
      if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
        return
      }
      // Grab the right to wake someone.
      // 如果能走到這,那就是上面的if判斷沒通過
      // 說明當(dāng)前鎖是空閑狀態(tài),但是等待隊(duì)列中有waiter,且沒有g(shù)oroutine被喚醒
      // 所以,這里我們想要把鎖的狀態(tài)設(shè)置為被喚醒,等待隊(duì)列waiter數(shù)-1
      new = (old - 1<<mutexWaiterShift) | mutexWoken
      if atomic.CompareAndSwapInt32(&m.state, old, new) {
        //通過信號(hào)量喚醒某一個(gè)waiter,退出
        runtime_Semrelease(&m.sema, false, 1)
        return
      }
      //失敗的話,更新old信息,進(jìn)入下個(gè)循環(huán)
      old = m.state
    }
  } else {
    // Starving mode: handoff mutex ownership to the next waiter, and yield
    // our time slice so that the next waiter can start to run immediately.
    // Note: mutexLocked is not set, the waiter will set it after wakeup.
    // But mutex is still considered locked if mutexStarving is set,
    // so new coming goroutines won't acquire it.
    //饑餓模式,喚醒等待隊(duì)列隊(duì)頭waiter
    runtime_Semrelease(&m.sema, true, 1)
  }
}

其他關(guān)鍵函數(shù)

runtime_canSpin

是否可自旋,不展開

runtime_doSpin

核心是匯編實(shí)現(xiàn),循環(huán)執(zhí)行三十次PAUSE指令

runtime_SemacquireMutex

信號(hào)量上鎖

sem來自單詞semaphore 信號(hào)量

runtime_Semrelease

信號(hào)量釋放

func runtime_Semrelease(s *uint32, handoff bool, skipframes int)
If handoff is true, pass count directly to the first waiter.

handoff 就是傳球的意思,handoff 為 false 時(shí),僅僅喚醒等待隊(duì)列中第一個(gè)協(xié)程,但是不會(huì)立馬調(diào)度該協(xié)程;當(dāng) handoff 為 true 時(shí),會(huì)立馬調(diào)度被喚醒的協(xié)程,此外,當(dāng) handoff = true 時(shí),被喚醒的協(xié)程會(huì)繼承當(dāng)前協(xié)程的時(shí)間片。具體例子,假設(shè)每個(gè) goroutine 的時(shí)間片為 2ms,gorounte A 已經(jīng)執(zhí)行了 1ms,假設(shè)它通過 runtime_Semrelease(handoff = true) 喚醒了 goroutine B,則 goroutine B 剩余的時(shí)間片為 2 - 1 = 1ms。

golang 中 sync.Mutex 的實(shí)現(xiàn)

semrelease1(addr, handoff, skipframes) 參數(shù)handoff若為true,則讓被喚醒的g立刻繼承當(dāng)前g的時(shí)間片繼續(xù)執(zhí)行。若handoff為false,則把剛被喚醒的g放到當(dāng)前p的runq中。

Golang sync.Mutex 源碼分析

RWMutex

很簡(jiǎn)單,看源碼就行

[Go并發(fā)] - RWMutex源碼解析

type RWMutex struct {
  w           Mutex  // held if there are pending writers
  writerSem   uint32 // semaphore for writers to wait for completing readers
  readerSem   uint32 // semaphore for readers to wait for completing writers
  readerCount int32  // number of pending readers 當(dāng)前讀鎖數(shù)量
  readerWait  int32  // number of departing readers 要離開的讀鎖數(shù)量,暨等待寫鎖解鎖,解鎖后可以釋放的讀鎖數(shù)量
}

Lock()

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
  if race.Enabled {
    _ = rw.w.state
    race.Disable()
  }
  // First, resolve competition with other writers.
  
  rw.w.Lock() //通過sync.Lock()限制多寫鎖進(jìn)入下邊的邏輯
  // Announce to readers there is a pending writer.
  //r值不變, rwmutexMaxReaders值為1<<30
  //可以理解為只要讀鎖的數(shù)量小于1<<30位,rw.readerCount值<0表示有寫鎖.
  //也可以理解為加上一個(gè)負(fù)數(shù),將31位以上都標(biāo)記為1,代表有寫鎖, 剩余30位記錄讀鎖數(shù)量
  r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
  // Wait for active readers.
  //r!=0 有讀鎖,不能釋放寫鎖
  //將readerCount轉(zhuǎn)移到readerWait,readerWait的新值!=0 (以上可以翻譯為有讀鎖,將讀鎖數(shù)轉(zhuǎn)移到讀等待數(shù),然后寫鎖阻塞,)
  // 滿足上面兩個(gè)條件,寫鎖阻塞, 等待喚醒,不返回
  if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
    runtime_SemacquireMutex(&rw.writerSem, false, 0)
  }
  if race.Enabled {
    race.Enable()
    race.Acquire(unsafe.Pointer(&rw.readerSem))
    race.Acquire(unsafe.Pointer(&rw.writerSem))
  }
}

UnLock()

// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
  if race.Enabled {
    _ = rw.w.state
    race.Release(unsafe.Pointer(&rw.readerSem))
    race.Disable()
  }
?
  // Announce to readers there is no active writer.\
  //將Lock()方法減去的值加回來,變成正數(shù)
  r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
  if r >= rwmutexMaxReaders {
    race.Enable()
    throw("sync: Unlock of unlocked RWMutex")
  }
  // Unblock blocked readers, if any.
  //喚醒在RLock()方法阻塞的讀操作,數(shù)量為r
  for i := 0; i < int(r); i++ {
    runtime_Semrelease(&rw.readerSem, false, 0)
  }
  // Allow other writers to proceed.
  rw.w.Unlock()
  if race.Enabled {
    race.Enable()
  }
}

RLock()

// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock. See the
// documentation on the RWMutex type.
func (rw *RWMutex) RLock() {
  if race.Enabled {
    _ = rw.w.state
    race.Disable()
  }
  //<0表示已上寫鎖,阻塞
  if atomic.AddInt32(&rw.readerCount, 1) < 0 {
    // A writer is pending, wait for it.
    runtime_SemacquireMutex(&rw.readerSem, false, 0)
  }
  if race.Enabled {
    race.Enable()
    race.Acquire(unsafe.Pointer(&rw.readerSem))
  }
}

UnRLock()

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
  if race.Enabled {
    _ = rw.w.state
    race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
    race.Disable()
  }
   //<0表示已上寫鎖,慢解鎖
  if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
    // Outlined slow-path to allow the fast-path to be inlined
    rw.rUnlockSlow(r)
  }
  if race.Enabled {
    race.Enable()
  }
}
?
// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) rUnlockSlow(r int32) {
  if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    race.Enable()
    throw("sync: RUnlock of unlocked RWMutex")
  }
  // A writer is pending.
  //最后一個(gè)讀等待,喚醒寫鎖
  if atomic.AddInt32(&rw.readerWait, -1) == 0 {
    // The last reader unblocks the writer.
    runtime_Semrelease(&rw.writerSem, false, 1)
  }
}

到此這篇關(guān)于一文帶你了解Go語(yǔ)言中鎖的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go泛型實(shí)戰(zhàn)教程之如何在結(jié)構(gòu)體中使用泛型

    Go泛型實(shí)戰(zhàn)教程之如何在結(jié)構(gòu)體中使用泛型

    這篇文章主要介紹了Go泛型實(shí)戰(zhàn)教程之如何在結(jié)構(gòu)體中使用泛型,根據(jù)Go泛型使用的三步曲提到的:類型參數(shù)化、定義類型約束、類型實(shí)例化我們一步步來定義我們的緩存結(jié)構(gòu)體,需要的朋友可以參考下
    2022-07-07
  • 基于原生Go語(yǔ)言開發(fā)一個(gè)博客系統(tǒng)

    基于原生Go語(yǔ)言開發(fā)一個(gè)博客系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了如何基于原生Go語(yǔ)言開發(fā)一個(gè)簡(jiǎn)單的博客系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-02-02
  • 淺談go 協(xié)程的使用陷阱

    淺談go 協(xié)程的使用陷阱

    這篇文章主要介紹了淺談go 協(xié)程的使用陷阱,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang線程池與協(xié)程池的使用

    Golang線程池與協(xié)程池的使用

    在Golang中,線程池和協(xié)程池是非常常見且重要的概念,它們可以提高應(yīng)用程序的并發(fā)處理能力和性能,減少資源的浪費(fèi),本文就來介紹一下Golang線程池與協(xié)程池的使用,感興趣的可以了解一下
    2024-04-04
  • golang官方嵌入文件到可執(zhí)行程序的示例詳解

    golang官方嵌入文件到可執(zhí)行程序的示例詳解

    這篇文章主要介紹了golang官方嵌入文件到可執(zhí)行程序,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • golang gin 監(jiān)聽rabbitmq隊(duì)列無(wú)限消費(fèi)的案例代碼

    golang gin 監(jiān)聽rabbitmq隊(duì)列無(wú)限消費(fèi)的案例代碼

    這篇文章主要介紹了golang gin 監(jiān)聽rabbitmq隊(duì)列無(wú)限消費(fèi),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • 使用Go語(yǔ)言連接和操作數(shù)據(jù)庫(kù)的基本步驟

    使用Go語(yǔ)言連接和操作數(shù)據(jù)庫(kù)的基本步驟

    在Go語(yǔ)言中,連接和操作數(shù)據(jù)庫(kù)通常使用database/sql包,它提供了一個(gè)數(shù)據(jù)庫(kù)抽象層,支持多種數(shù)據(jù)庫(kù)引擎,如MySQL、PostgreSQL、SQLite等,下面我將以MySQL為例,詳細(xì)講解如何使用Go語(yǔ)言連接和操作數(shù)據(jù)庫(kù),需要的朋友可以參考下
    2024-06-06
  • Go?項(xiàng)目目錄布局保姆級(jí)教程

    Go?項(xiàng)目目錄布局保姆級(jí)教程

    這篇文章主要為大家介紹了Go?項(xiàng)目目錄布局保姆級(jí)教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語(yǔ)言如何實(shí)現(xiàn)TCP通信詳解

    Go語(yǔ)言如何實(shí)現(xiàn)TCP通信詳解

    go里面實(shí)現(xiàn)tcp沒有像之前寫的C++那些那么麻煩,在C++里面要先創(chuàng)建套接字,然后綁定ip地址,go里面直接就一個(gè)函數(shù)建立套接字,然后在進(jìn)行通信就可以了,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言如何實(shí)現(xiàn)TCP通信的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Go語(yǔ)言中defer使用的陷阱小結(jié)

    Go語(yǔ)言中defer使用的陷阱小結(jié)

    本文主要介紹了Go語(yǔ)言中defer使用的陷阱小結(jié),分別是defer語(yǔ)句不可以在return語(yǔ)句之后,defer語(yǔ)句執(zhí)行的匿名函數(shù),匿名函數(shù)的參數(shù)會(huì)被預(yù)先處理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01

最新評(píng)論