亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文帶你讀懂Golang?sync包之sync.Mutex

 更新時(shí)間:2023年04月19日 09:27:17   作者:卡布奇諾賽高  
sync.Mutex可以說是sync包的核心了,?sync.RWMutex,?sync.WaitGroup...都依賴于他,?本章我們將帶你一文讀懂sync.Mutex,快跟隨小編一起學(xué)習(xí)一下吧

sync.Mutex可以說是sync包的核心了, sync.RWMutex, sync.WaitGroup...都依賴于他, 本章我們將帶你一文讀懂sync.Mutex. 我們主要介紹如下內(nèi)容

  • sync.Mutex數(shù)據(jù)結(jié)構(gòu)
  • 為什么sync.Mutex不需要初始化
  • 正常模式和饑餓模式
  • sync.Mutex的三大方法, Lock(), UnLock(), TryLock()

sync.Mutex的數(shù)據(jù)結(jié)構(gòu)

type Mutex struct {
   state int32
   sema  uint32
}

我們可以發(fā)現(xiàn)sync.Mutex的數(shù)據(jù)結(jié)構(gòu)十分簡單, 他只有兩個(gè)字段

  • state: 一個(gè)32位整數(shù), 表示了當(dāng)前鎖的狀態(tài)
  • sema: 他代表一個(gè)信號量(信號量是一個(gè)無符號整數(shù),OS對信號量提供了能使其原子性自增/自減的PV操作, 可以通過信號量來實(shí)現(xiàn)互斥)

state

state的含義如圖

他的低三位分別是Locked, Woken, Starving. 剩余28位表示在當(dāng)前Mutex中阻塞的goroutine數(shù)目

  • Locked: 當(dāng)前Mutex是否處于上鎖狀態(tài), 0: 上鎖, 1: 上鎖.
  • Woken: 當(dāng)前Mutex是否存在goroutine被喚醒, 0: 沒有, 1: 存在被喚醒的goroutnine正在上鎖.
  • Starving: 當(dāng)Mutex處于何種模式, 0: 正常模式, 1: 饑餓模式

為什么sync.Mutex不需要初始化

當(dāng)我們聲明一個(gè)變量但不給他賦值時(shí), 他會被默認(rèn)附上初始值, 這個(gè)初始值對于指針類型是nil, 而其他類型則是其零值.

因此, 當(dāng)我們僅聲明sync.Mutex時(shí), 他會被默認(rèn)賦值{state:0,sema:0}, 而這個(gè)值恰好表示初始的鎖狀態(tài). 所以 ,我們可以僅在聲明后直接使用sync.Mutex.

正常模式和饑餓模式

在了解正常模式和饑餓模式前, 我們先來看下?lián)屨际胶头菗屨际?

搶占式和非搶占式是調(diào)度的兩種方式

搶占式: 當(dāng)一個(gè)新goroutine請求鎖時(shí), 他會和當(dāng)前被喚醒的goroutine進(jìn)行競爭, 競爭成功就獲取鎖. 非搶占式: 如果阻塞隊(duì)列中存在有其他goroutine, 那么新請求鎖的goroutine會直接進(jìn)入阻塞隊(duì)列排隊(duì).

一般情況下, sync.Mutex處于正常模式, 在該模式下, 鎖的獲取方式為搶占式調(diào)度. 而一般情況下, 因?yàn)樾抡埱箧i的goroutine正在持有CPU且可能不止一個(gè), 這就導(dǎo)致阻塞隊(duì)列中新被喚醒的goroutine是難以搶占過新請求鎖的goroutine的, 從而導(dǎo)致饑餓現(xiàn)象.

為了解決這種饑餓現(xiàn)象, go語言在go1.9的時(shí)候引入了饑餓模式, 在饑餓模式下, 鎖的獲取方式為非搶占式調(diào)度.

正常模式->饑餓模式:

當(dāng)一個(gè)goroutine在阻塞隊(duì)列等待超過1ms后, 他就會修改state使鎖的狀態(tài)變?yōu)轲囸I模式

饑餓模式->正常模式:

  • 當(dāng)阻塞隊(duì)列中某個(gè)goroutine阻塞時(shí)間小于1ms時(shí)
  • 阻塞隊(duì)列中重新變?yōu)榭諘r(shí)

sync.Mutex三大方法

Lock()

Lock方法有兩種上鎖方式, 快速上鎖和慢速上鎖

快速上鎖

假如當(dāng)前sync.Mutex的state=0, 那么就意味著當(dāng)前sync.Mutex尚未被任何goroutine持有且阻塞隊(duì)列中沒有任何goroutine.

那么當(dāng)前請求鎖的goroutine就會通過CAS的方式修改state=1(意味著上鎖), 然后返回.

慢速上鎖

否則, 會調(diào)用lockSlow()方法來慢速上鎖.

首先嘗試通過自旋的方式獲取鎖, 在如下四個(gè)條件全部滿足時(shí)會繼續(xù)自旋, 如果成功通過自旋獲取鎖, 就直接返回

  • state不處于饑餓態(tài)
  • 自旋次數(shù)小于4次
  • 當(dāng)前進(jìn)程運(yùn)行于多CPU主機(jī)
  • 存在至少一個(gè)正在運(yùn)行且工作隊(duì)列為空的控制器P

之后會根據(jù)當(dāng)前鎖的不同狀態(tài)做出不同的行為, 這里我們分類討論.

正常狀態(tài)

我們只截取少量的核心代碼.

//如果處于正常態(tài), 那么我們修改新狀態(tài)為上鎖
if old&mutexStarving == 0 {
    new |= mutexLocked 
}

//通過cas的方式修改當(dāng)前鎖狀態(tài)
if atomic.CompareAndSwapInt32(&m.state, old, new) {
        //如果舊狀態(tài)處于正常態(tài)且未上鎖, 那么就意味著當(dāng)前線程搶占到了鎖, 直接返回
	if old&(mutexLocked|mutexStarving) == 0 {
		break //這里的break可以理解為return, 不用return是因?yàn)樽詈笥袀€(gè)對race.Enabled的判斷, 用于競態(tài)檢測
	}
        ......
}

如果處于正常狀態(tài), 那么允許鎖搶占, 我們先把新的鎖狀態(tài)修改為直接上鎖, 這是因?yàn)榧偃缢疤幱谏湘i態(tài), 那么之后也會處于上鎖態(tài), 而如果處于未上鎖狀態(tài), 那么當(dāng)前goroutine會為其上鎖, 因此鎖的新狀態(tài)一定處于上鎖態(tài).

然后我們通過CAS的方式用新狀態(tài)替換舊狀態(tài). 替換成功后判斷old(更新前的狀態(tài))是否處于正常態(tài)且未上鎖, 如果是, 那么說明是當(dāng)前goroutine上的鎖, 這也就意味著當(dāng)前goroutine成功獲取鎖了, 那么直接返回.

否則會調(diào)用runtime_SemacquireMutex來在sema信號量下阻塞當(dāng)前goroutine.

饑餓狀態(tài)

饑餓狀態(tài)會直接調(diào)用runtime_SemacquireMutex來在sema信號量下阻塞當(dāng)前goroutine.

被喚醒后

在某個(gè)goroutine被喚醒后, 他會判斷自己阻塞時(shí)間是否超過1ms, 如果超過, 則切換為饑餓模式, 否則判斷自己是否處于饑餓模式且阻塞的時(shí)間小于1ms, 如果處于饑餓模式且阻塞時(shí)間小于1ms, 那么就退出饑餓模式.

Unlock()

unlock()方法也分為快速解鎖和慢速解鎖兩部分

快速解鎖

我們直接讓new=當(dāng)前狀態(tài)-mutexLocked如果new=0, 則意味著只有當(dāng)前goroutine在持有鎖, 且無任何goroutine在等待鎖, 那么直接CAS修改m.state=new然后返回即可.

慢速解鎖

否則調(diào)用unlockSlow()函數(shù)來解鎖, unlockSlow()函數(shù)也會根據(jù)當(dāng)前Mutex的不同狀態(tài)做出不同的行為

不過首先, unlockSlow()會判斷當(dāng)前Mutex是否處于上鎖態(tài), 如果我們對未上鎖的Mutex調(diào)用Unlock()函數(shù), 會爆出sync: unlock of unlocked mutex的panic.

正常狀態(tài)

通過state的前28位判斷當(dāng)前等待鎖的goroutine是否為0, 如果是, 那么直接解鎖返回.

否則通過CAS的方式修改當(dāng)前Mutex狀態(tài)為new, 如果修改成功, 那么將釋放一個(gè)信號量來隨機(jī)喚醒一個(gè)阻塞在sema中的goroutine

//false意味著隨機(jī)喚醒
runtime_Semrelease(&m.sema, false, 1)

饑餓狀態(tài)

如果當(dāng)前Mutex處于饑餓狀態(tài), 那么說明一定存在阻塞的goroutine, 將釋放一個(gè)信號量來喚醒sema中第一個(gè)阻塞的goroutine

//true為順序喚醒
runtime_Semrelease(&m.sema, true, 1)

TryLock()

TryLock是嘗試上鎖, 如果上鎖成功, 返回true, 否則返回false, 他的實(shí)現(xiàn)十分簡單.

  • 判斷當(dāng)前狀態(tài)Mutex的狀態(tài)是否是饑餓態(tài)或者已上鎖, 如果是, 則直接返回false
  • 通過CAS的方式嘗試為Mutex上鎖, 上鎖成功則返回true,否則返回false

到此這篇關(guān)于一文帶你讀懂Golang sync包之sync.Mutex的文章就介紹到這了,更多相關(guān)Golang sync.Mutex內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中map集合的具體使用

    Go語言中map集合的具體使用

    本文主要介紹了Go語言中map集合的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Golang channel關(guān)閉的實(shí)現(xiàn)示例

    Golang channel關(guān)閉的實(shí)現(xiàn)示例

    channel關(guān)閉不當(dāng)或不關(guān)閉會引發(fā)很多問題,本文主要介紹了Golang channel關(guān)閉的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Golang新提案:panic?能不能加個(gè)?PanicError?

    Golang新提案:panic?能不能加個(gè)?PanicError?

    這篇文章主要為大家介紹了Golang的新提案關(guān)于panic能不能加個(gè)PanicError的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 在?Go?語言中使用?regexp?包處理正則表達(dá)式的操作

    在?Go?語言中使用?regexp?包處理正則表達(dá)式的操作

    正則表達(dá)式是處理字符串時(shí)一個(gè)非常強(qiáng)大的工具,而?Go?語言的?regexp?包提供了簡單而強(qiáng)大的接口來使用正則表達(dá)式,本文將介紹如何在?Go?中使用?regexp?包來編譯和執(zhí)行正則表達(dá)式,以及如何從文本中匹配和提取信息,感興趣的朋友一起看看吧
    2023-12-12
  • 關(guān)于golang監(jiān)聽rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接的問題

    關(guān)于golang監(jiān)聽rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接的問題

    這篇文章主要介紹了golang監(jiān)聽rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • golang配置文件解析器之goconfig框架的使用詳解

    golang配置文件解析器之goconfig框架的使用詳解

    goconfig是一個(gè)易于使用,支持注釋的 Go 語言配置文件解析器,該文件的書寫格式和 Windows 下的 INI 文件一樣,本文主要為大家介紹了goconfig框架的具體使用,需要的可以參考下
    2023-11-11
  • golang程序使用alpine編譯出最小arm鏡像實(shí)現(xiàn)

    golang程序使用alpine編譯出最小arm鏡像實(shí)現(xiàn)

    這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語言處理超大字符串型整數(shù)加減經(jīng)典面試詳解

    Go語言處理超大字符串型整數(shù)加減經(jīng)典面試詳解

    這篇文章主要為大家介紹了Go語言處理超大字符串型整數(shù)加減經(jīng)典面試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 在ubuntu下安裝go開發(fā)環(huán)境的全過程

    在ubuntu下安裝go開發(fā)環(huán)境的全過程

    Go語言是谷歌公司開發(fā)的編程語言,雖然安裝和配置go很簡單,但是很多初學(xué)者在第一次安裝go環(huán)境時(shí)會遇到各種坑,下面這篇文章主要給大家介紹了關(guān)于在ubuntu下安裝go開發(fā)環(huán)境的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Golang表示枚舉類型的詳細(xì)講解

    Golang表示枚舉類型的詳細(xì)講解

    go 語言枚舉類型是這么用的?在什么場景下會用到枚舉?本文對 go 語言枚舉做了詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧
    2021-09-09

最新評論