go 并发安全map 分段锁实现
生活随笔
收集整理的這篇文章主要介紹了
go 并发安全map 分段锁实现
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一. 簡(jiǎn)言
1.1 go中的map不是并發(fā)安全的
1.2 go1.9版本之前,可以使用map+mutex的方式實(shí)現(xiàn)并發(fā)安全,但是每次操作,無(wú)論讀取都要加鎖,性能不太好
1.3 go 1.9之后,新增了sync.Map,是并發(fā)安全的,效率也很高,具體的源碼分析可見(jiàn)筆者的另外一篇博客
? ? ? ??https://blog.csdn.net/yzf279533105/article/details/98108367
1.4?類(lèi)似java的ConcurrentHashMap的實(shí)現(xiàn),可以對(duì)key進(jìn)行分段,一個(gè)段內(nèi)使用一個(gè)鎖,這樣操作不同的key時(shí),避免鎖的阻塞開(kāi) ? ? ? ?銷(xiāo),大大提高效率,這篇博客我們實(shí)現(xiàn)的就是go語(yǔ)言的分段鎖
二. 效率驗(yàn)證(與map+鎖,sync.map的性能對(duì)比)
參見(jiàn)筆者的另外一篇博客
concurrentmap,sync.map,map+鎖的效率比對(duì)
三. 代碼實(shí)現(xiàn)
// 總的map type ConcurrentMap []*ConcurrentMapShared// 默認(rèn)分片數(shù) const SHARE_COUNT int = 64// 單個(gè)map分片 type ConcurrentMapShared struct {items map[string]interface{} // 本分片內(nèi)的mapmu sync.RWMutex // 本分片的專(zhuān)用鎖 }// 新建一個(gè)map func NewConcurrentMap() *ConcurrentMap {m := make(ConcurrentMap, SHARE_COUNT)for i := 0; i < SHARE_COUNT; i++ {m[i] = &ConcurrentMapShared{items: map[string]interface{}{},}}return &m }// GetSharedMap 獲取key對(duì)應(yīng)的map分片 func (m ConcurrentMap) GetSharedMap(key string) *ConcurrentMapShared {return m[uint(fnv32(key))%uint(SHARE_COUNT)] }// hash函數(shù) func fnv32(key string) uint32 {hash := uint32(2166136261)prime32 := uint32(16777619)for i := 0; i < len(key); i++ {hash *= prime32hash ^= uint32(key[i])}return hash }// Set 設(shè)置key,value func (m ConcurrentMap) Set(key string, value interface{}) {sharedMap := m.GetSharedMap(key) // 找到對(duì)應(yīng)的分片mapsharedMap.mu.Lock() // 加鎖(全鎖定)sharedMap.items[key] = value // 賦值sharedMap.mu.Unlock() // 解鎖 }// Get 獲取key對(duì)應(yīng)的value func (m ConcurrentMap) Get(key string) (value interface{}, ok bool) {sharedMap := m.GetSharedMap(key) // 找到對(duì)應(yīng)的分片mapsharedMap.mu.RLock() // 加鎖(讀鎖定)value, ok = sharedMap.items[key] // 取值sharedMap.mu.RUnlock() // 解鎖return value, ok }// Count 統(tǒng)計(jì)key個(gè)數(shù) func (m ConcurrentMap) Count() int {count := 0for i := 0; i < SHARE_COUNT; i++ {m[i].mu.RLock() // 加鎖(讀鎖定)count += len(m[i].items)m[i].mu.RUnlock() // 解鎖}return count }// Keys1 所有的key方法1(方法:遍歷每個(gè)分片map,讀取key;缺點(diǎn):量大時(shí),阻塞時(shí)間較長(zhǎng)) func (m ConcurrentMap) Keys1() []string {count := m.Count()keys := make([]string, count)// 遍歷所有的分片mapfor i := 0; i < SHARE_COUNT; i++ {m[i].mu.RLock() // 加鎖(讀鎖定)oneMapKeys := make([]string, len(m[i].items))for k := range m[i].items {oneMapKeys = append(oneMapKeys, k)}m[i].mu.RUnlock() // 解鎖// 匯總到keyskeys = append(keys, oneMapKeys...)}return keys }// Keys2 所有的key方法2(方法:開(kāi)多個(gè)協(xié)程分別對(duì)分片map做統(tǒng)計(jì)再匯總 優(yōu)點(diǎn):量大時(shí),阻塞時(shí)間較短) func (m ConcurrentMap) Keys2() []string {count := m.Count()keys := make([]string, count)ch := make(chan string, count) // 通道,遍歷時(shí)// 單獨(dú)起一個(gè)協(xié)程go func() {wg := sync.WaitGroup{}wg.Add(SHARE_COUNT)for i := 0; i < SHARE_COUNT; i++ {// 每個(gè)分片map,單獨(dú)起一個(gè)協(xié)程進(jìn)行統(tǒng)計(jì)go func(ms *ConcurrentMapShared) {defer wg.Done()ms.mu.RLock() // 加鎖(讀鎖定)for k := range ms.items {ch <- k // 壓入通道}ms.mu.RUnlock() // 解鎖}(m[i])}// 等待所有協(xié)程執(zhí)行完畢wg.Wait()close(ch) // 一定要關(guān)閉通道,因?yàn)椴魂P(guān)閉的話(huà),后面的range不會(huì)結(jié)束!!!}()// 遍歷通道,壓入所有的keyfor k := range ch {keys = append(keys, k)}return keys }?
總結(jié)
以上是生活随笔為你收集整理的go 并发安全map 分段锁实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: go sync.map 源码分析
- 下一篇: go 关闭通道的必要性