<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
sync.Mutex
是Go中的互斥鎖,通過.lock()
方法上鎖,.unlock()
方法解鎖。需要注意的是,因為Go函數值傳遞的特點,sync.Mutex
通過函數傳遞時,會進行一次拷貝,所以傳遞過去的鎖是一把全新的鎖,大家在使用時要注意這一點,另外sync.Mutex
是非重入鎖,這一點要與Java中的鎖區分。
type Mutex { state int32 sema uint32 }
上面資料結構中的state
最低三位分別表示 mutexLocked、mutexWoken 和 mutexStarving,剩下的位置用來表示當前有多少個 Goroutine 等待互斥鎖的釋放:
32 3 2 1 0 | | | | | | | | | | v-----------------------------------------------v-------------v-------------v-------------+ | | | | v | waitersCount |mutexStarving| mutexWoken | mutexLocked | | | | | | +-----------------------------------------------+-------------+-------------+-------------+
type RWMutex struct { w Mutex // 複用互斥鎖 writerSem uint32 // 寫鎖監聽讀鎖釋放的號誌 readerSem uint32 // 讀鎖監聽寫鎖釋放的號誌 readerCount int32 // 當前正在執行讀操作的數量 readerWait int32 // 當寫操作被阻塞時,需要等待讀操作完成的個數 }
RLock(): 申請讀鎖,每次執行此函數後,會對readerCount++,此時當有寫操作執行Lock()時會判斷readerCount>0,就會阻塞。
RUnLock(): 解除讀鎖,執行readerCount–,釋放號誌喚醒等待寫操作的goroutine。
Lock(): 申請寫鎖,獲取互斥鎖,此時會阻塞其他的寫操作。並將readerCount 置為 -1,當有讀操作進來,發現readerCount = -1, 即知道有寫操作在進行,阻塞。
Unlock(): 解除寫鎖,會先通知所有阻塞的讀操作goroutine,然後才會釋放持有的互斥鎖。
這是由於寫操作要等待讀操作結束後才可以獲得鎖,而寫操作在等待期間可能還有新的讀操作持續到來,如果寫操作等待所有讀操作結束,很可能會一直阻塞,這種現象稱之為寫操作被餓死。
通過RWMutex結構體中的readerWait屬性可完美解決這個問題。
當寫操作到來時,會把RWMutex.readerCount值拷貝到RWMutex.readerWait中,用於標記排在寫操作前面的讀者個數。
前面的讀操作結束後,除了會遞減RWMutex.readerCount,還會遞減RWMutex.readerWait值,當RWMutex.readerWait值變為0時喚醒寫操作。
一般情況下解決並行讀寫 map 的思路是加一把大鎖,或者把一個 map 分成若干個小 map,對 key 進行雜湊,只操作相應的小 map。前者鎖的粒度比較大,影響效率;後者實現起來比較複雜,容易出錯。
而使用 sync.map
之後,對 map 的讀寫,不需要加鎖。並且它通過空間換時間的方式,使用 read 和 dirty 兩個 map 來進行讀寫分離,降低鎖時間來提高效率。
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{} }
在進行讀操作的時候,會先在read中找,沒有命中的話會鎖住dirty並且尋找,如果找到了miss計數+1,超過閾值時將dirty賦值給read;
在進行新增操作時,直接在dirty中新增;
在進行修改操作時,先改read,再改dirty;
在進行刪除操作時,將read中加上amended標記,dirty中直接刪除。
願此操作的底層是靠 MESI 快取一致性協定來維持的。
Go的 atomic.Value 需要注意應該放入唯讀物件。
//atomic.Value原始碼 type Value struct { v interface{} // 所以可以儲存任何型別的資料 } // 空 interface{} 的內部表示格式,作用是將interface{}型別分解,得到其中兩個欄位 type ifaceWords struct { typ unsafe.Pointer data unsafe.Pointer } // 取資料就是正常走流程 func (v *Value) Load() (x interface{}) { vp := (*ifaceWords)(unsafe.Pointer(v)) typ := LoadPointer(&vp.typ) if typ == nil || uintptr(typ) == ^uintptr(0) { // 第一次還沒寫入 return nil } // 構造新的interface{}返回出去 data := LoadPointer(&vp.data) xp := (*ifaceWords)(unsafe.Pointer(&x)) xp.typ = typ xp.data = data return } // 寫資料(如何保證資料完整性) func (v *Value) Store(x interface{}) { if x == nil { panic("sync/atomic: store of nil value into Value") } // 繞過 Go 語言型別系統的檢查,與任意的指標型別互相轉換 vp := (*ifaceWords)(unsafe.Pointer(v)) // 舊值 xp := (*ifaceWords)(unsafe.Pointer(&x)) // 新值 for { // 配合CompareAndSwap達到樂觀鎖的功效 typ := LoadPointer(&vp.typ) if typ == nil { // 第一次寫入 runtime_procPin() // 禁止搶佔 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { runtime_procUnpin() // 沒有搶到鎖,說明已經有別的執行緒搶先完成賦值,重新進入迴圈 continue } // 首次賦值 StorePointer(&vp.data, xp.data) StorePointer(&vp.typ, xp.typ) runtime_procUnpin() // 寫入成功,解除佔用狀態 return } if uintptr(typ) == ^uintptr(0) { // 第一次寫入還未完成,繼續等待 continue } // 兩次需要寫入相同型別 if typ != xp.typ { panic("sync/atomic: store of inconsistently typed value into Value") } StorePointer(&vp.data, xp.data) return } } // 禁止搶佔,標記當前G在M上不會被搶佔,並返回當前所在P的ID。 func runtime_procPin() // 解除G的禁止搶佔狀態,之後G可被搶佔。 func runtime_procUnpin()
var m sync.Mutex func DoSth() { // do sth1 func() { u.lock() defer m.unlock() // do sth2 }() // do sth3 }
如上所示,如果do sth3中是很費時的io操作,使用這個技巧可以將臨界區減小,提高效能,不過,如果本身臨界區就不大,鎖操作後續沒有什麼費時操作,那麼也就沒有必要這樣操作了。
在高並行場景下,用鎖的數量來換取並行效率,類似於java中ConcurrentHashmap的分段鎖思想,增加鎖的數量,減少一把鎖控制的資料量。
在讀多寫少的情景下,可以使用讀寫鎖,提高讀操作的並行效能。
原子操作是CPU指令級的操作,不會觸發g排程機制。,不阻塞執行流
到此這篇關於Golang的鎖機制與使用技巧精選的文章就介紹到這了,更多相關Golang 鎖機制內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45