網(wǎng)站首頁 編程語言 正文
1. sync.Mutex詳解
sync.Mutex
是Go中的互斥鎖,通過.lock()
方法上鎖,.unlock()
方法解鎖。需要注意的是,因?yàn)镚o函數(shù)值傳遞的特點(diǎn),sync.Mutex
通過函數(shù)傳遞時(shí),會(huì)進(jìn)行一次拷貝,所以傳遞過去的鎖是一把全新的鎖,大家在使用時(shí)要注意這一點(diǎn),另外sync.Mutex
是非重入鎖,這一點(diǎn)要與Java中的鎖區(qū)分。
type Mutex { state int32 sema uint32 }
上面數(shù)據(jù)結(jié)構(gòu)中的state
最低三位分別表示 mutexLocked、mutexWoken 和 mutexStarving,剩下的位置用來表示當(dāng)前有多少個(gè) Goroutine 等待互斥鎖的釋放:
32 3 2 1 0 | | | | | | | | | | v-----------------------------------------------v-------------v-------------v-------------+ | | | | v | waitersCount |mutexStarving| mutexWoken | mutexLocked | | | | | | +-----------------------------------------------+-------------+-------------+-------------+
- mutexLocked — 表示互斥鎖的鎖定狀態(tài);
- mutexWoken — 表示從正常模式被從喚醒;
- mutexStarving — 當(dāng)前的互斥鎖進(jìn)入饑餓狀態(tài);
- waitersCount — 當(dāng)前互斥鎖上等待的 goroutine 個(gè)數(shù);
2. RWMutex詳解
type RWMutex struct { w Mutex // 復(fù)用互斥鎖 writerSem uint32 // 寫鎖監(jiān)聽讀鎖釋放的信號(hào)量 readerSem uint32 // 讀鎖監(jiān)聽寫鎖釋放的信號(hào)量 readerCount int32 // 當(dāng)前正在執(zhí)行讀操作的數(shù)量 readerWait int32 // 當(dāng)寫操作被阻塞時(shí),需要等待讀操作完成的個(gè)數(shù) }
- 讀操作如何防止并發(fā)讀寫問題的?
RLock(): 申請讀鎖,每次執(zhí)行此函數(shù)后,會(huì)對(duì)readerCount++,此時(shí)當(dāng)有寫操作執(zhí)行Lock()時(shí)會(huì)判斷readerCount>0,就會(huì)阻塞。
RUnLock(): 解除讀鎖,執(zhí)行readerCount–,釋放信號(hào)量喚醒等待寫操作的goroutine。
- 寫操作如何防止并發(fā)讀寫、并發(fā)寫寫問題?
Lock(): 申請寫鎖,獲取互斥鎖,此時(shí)會(huì)阻塞其他的寫操作。并將readerCount 置為 -1,當(dāng)有讀操作進(jìn)來,發(fā)現(xiàn)readerCount = -1, 即知道有寫操作在進(jìn)行,阻塞。
Unlock(): 解除寫鎖,會(huì)先通知所有阻塞的讀操作goroutine,然后才會(huì)釋放持有的互斥鎖。
- 寫操作的饑餓問題?
這是由于寫操作要等待讀操作結(jié)束后才可以獲得鎖,而寫操作在等待期間可能還有新的讀操作持續(xù)到來,如果寫操作等待所有讀操作結(jié)束,很可能會(huì)一直阻塞,這種現(xiàn)象稱之為寫操作被餓死。
通過RWMutex結(jié)構(gòu)體中的readerWait屬性可完美解決這個(gè)問題。
當(dāng)寫操作到來時(shí),會(huì)把RWMutex.readerCount值拷貝到RWMutex.readerWait中,用于標(biāo)記排在寫操作前面的讀者個(gè)數(shù)。
前面的讀操作結(jié)束后,除了會(huì)遞減RWMutex.readerCount,還會(huì)遞減RWMutex.readerWait值,當(dāng)RWMutex.readerWait值變?yōu)?時(shí)喚醒寫操作。
3. sync.Map詳解
一般情況下解決并發(fā)讀寫 map 的思路是加一把大鎖,或者把一個(gè) map 分成若干個(gè)小 map,對(duì) key 進(jìn)行哈希,只操作相應(yīng)的小 map。前者鎖的粒度比較大,影響效率;后者實(shí)現(xiàn)起來比較復(fù)雜,容易出錯(cuò)。
而使用?sync.map
?之后,對(duì) map 的讀寫,不需要加鎖。并且它通過空間換時(shí)間的方式,使用 read 和 dirty 兩個(gè) map 來進(jìn)行讀寫分離,降低鎖時(shí)間來提高效率。
type Map struct { mu Mutex read atomic.Value // readOnly dirty map[interface{}]*entry misses int } // readOnly is an immutable struct stored atomically in the Map.read field. type readOnly struct { m map[interface{}]*entry amended bool // true if the dirty map contains some key not in m. } type entry struct { p unsafe.Pointer // *interface{} }
在進(jìn)行讀操作的時(shí)候,會(huì)先在read中找,沒有命中的話會(huì)鎖住dirty并且尋找,如果找到了miss計(jì)數(shù)+1,超過閾值時(shí)將dirty賦值給read;
在進(jìn)行添加操作時(shí),直接在dirty中添加;
在進(jìn)行修改操作時(shí),先改read,再改dirty;
在進(jìn)行刪除操作時(shí),將read中加上amended標(biāo)記,dirty中直接刪除。
4. 原子操作 atomic.Value
愿此操作的底層是靠 MESI 緩存一致性協(xié)議來維持的。
Go的 atomic.Value 需要注意應(yīng)該放入只讀對(duì)象。
//atomic.Value源碼 type Value struct { v interface{} // 所以可以存儲(chǔ)任何類型的數(shù)據(jù) } // 空 interface{} 的內(nèi)部表示格式,作用是將interface{}類型分解,得到其中兩個(gè)字段 type ifaceWords struct { typ unsafe.Pointer data unsafe.Pointer } // 取數(shù)據(jù)就是正常走流程 func (v *Value) Load() (x interface{}) { vp := (*ifaceWords)(unsafe.Pointer(v)) typ := LoadPointer(&vp.typ) if typ == nil || uintptr(typ) == ^uintptr(0) { // 第一次還沒寫入 return nil } // 構(gòu)造新的interface{}返回出去 data := LoadPointer(&vp.data) xp := (*ifaceWords)(unsafe.Pointer(&x)) xp.typ = typ xp.data = data return } // 寫數(shù)據(jù)(如何保證數(shù)據(jù)完整性) func (v *Value) Store(x interface{}) { if x == nil { panic("sync/atomic: store of nil value into Value") } // 繞過 Go 語言類型系統(tǒng)的檢查,與任意的指針類型互相轉(zhuǎn)換 vp := (*ifaceWords)(unsafe.Pointer(v)) // 舊值 xp := (*ifaceWords)(unsafe.Pointer(&x)) // 新值 for { // 配合CompareAndSwap達(dá)到樂觀鎖的功效 typ := LoadPointer(&vp.typ) if typ == nil { // 第一次寫入 runtime_procPin() // 禁止搶占 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { runtime_procUnpin() // 沒有搶到鎖,說明已經(jīng)有別的線程搶先完成賦值,重新進(jìn)入循環(huán) continue } // 首次賦值 StorePointer(&vp.data, xp.data) StorePointer(&vp.typ, xp.typ) runtime_procUnpin() // 寫入成功,解除占用狀態(tài) return } if uintptr(typ) == ^uintptr(0) { // 第一次寫入還未完成,繼續(xù)等待 continue } // 兩次需要寫入相同類型 if typ != xp.typ { panic("sync/atomic: store of inconsistently typed value into Value") } StorePointer(&vp.data, xp.data) return } } // 禁止搶占,標(biāo)記當(dāng)前G在M上不會(huì)被搶占,并返回當(dāng)前所在P的ID。 func runtime_procPin() // 解除G的禁止搶占狀態(tài),之后G可被搶占。 func runtime_procUnpin()
5. 使用小技巧
- 減小臨界區(qū)域(減少鎖的持有時(shí)間)
var m sync.Mutex func DoSth() { // do sth1 func() { u.lock() defer m.unlock() // do sth2 }() // do sth3 }
如上所示,如果do sth3中是很費(fèi)時(shí)的io操作,使用這個(gè)技巧可以將臨界區(qū)減小,提高性能,不過,如果本身臨界區(qū)就不大,鎖操作后續(xù)沒有什么費(fèi)時(shí)操作,那么也就沒有必要這樣操作了。
- 減小鎖的粒度
在高并發(fā)場景下,用鎖的數(shù)量來換取并發(fā)效率,類似于java中ConcurrentHashmap的分段鎖思想,增加鎖的數(shù)量,減少一把鎖控制的數(shù)據(jù)量。
- 讀寫分離(讀寫鎖): RWMutex,sync.Map
在讀多寫少的情景下,可以使用讀寫鎖,提高讀操作的并發(fā)性能。
- 使用原子操作
原子操作是CPU指令級(jí)的操作,不會(huì)觸發(fā)g調(diào)度機(jī)制。,不阻塞執(zhí)行流
原文鏈接:https://juejin.cn/post/7103935276564611086
相關(guān)推薦
- 2022-04-19 C語言庫函數(shù)qsort及bsearch快速排序算法使用解析_C 語言
- 2022-05-12 Kotlin List的創(chuàng)建與取值 getOrElse getOrNull
- 2022-05-03 python實(shí)現(xiàn)跨進(jìn)程(跨py文件)通信示例_python
- 2022-10-14 eclipse創(chuàng)建maven項(xiàng)目
- 2022-12-15 網(wǎng)站壓力測試工具-ab工具apache?bench使用過程_服務(wù)器其它
- 2022-10-31 Golang?template?包基本原理分析_Golang
- 2022-06-19 python繪制橫豎條形圖的方法_python
- 2022-01-11 我不會(huì)ES6-數(shù)據(jù)類型轉(zhuǎn)換-一個(gè)對(duì)象變?yōu)閷?duì)象數(shù)組 Object.keys(obj)
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支