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