看过这篇剖析,你还不懂 Go sync.Map 吗?

hi, 大家好,我是 haohongfan。

本篇文章会从使用方式和原码角度剖析 sync.Map。不过不管是日常开发还是开源项目中,好像 sync.Map 并没有得到很好的利用,大家还是习惯使用 Mutex + Map 来使用。

下面这段代码,看起来很有道理,其实是用错了(背景:并发场景中获取注册信息)。

instance, ok := instanceMap[name] if ok { return instance, nil } initLock.Lock() defer initLock.Unlock() // double check instance, ok = instanceMap[name] if ok { return instance, nil }

这里使用使用 sync.Map 会更合理些,因为 sync.Map 底层完全包含了这个逻辑。可能写 Java 的同学看着上面这段代码很眼熟,但确实是用错了,关于为什么用错了以及会造成什么影响,请大家关注后续的文章。

我大概分析了下大家宁愿使用 Mutex + Map,也不愿使用 sync.Map 的原因:

sync.Map 本身就很难用,使用起来并不像一个 Map。失去了 map 应有的特权语法,如:make, map[1] 等

sync.Map 方法较多。让一个简单的 Map 使用起来有了较高的学习成本。

不管什么样的原因吧,当你读过这篇文章后,在某些特定的并发场景下,建议使用 sync.Map 代替 Map + Mutex 的。

用法全解 package main import ( "fmt" "sync" ) func main() { var syncMap sync.Map syncMap.Store("11", 11) syncMap.Store("22", 22) fmt.Println(syncMap.Load("11")) // 11 fmt.Println(syncMap.Load("33")) // 空 fmt.Println(syncMap.LoadOrStore("33", 33)) // 33 fmt.Println(syncMap.Load("33")) // 33 fmt.Println(syncMap.LoadAndDelete("33")) // 33 fmt.Println(syncMap.Load("33")) // 空 syncMap.Range(func(key, value interface{}) bool { fmt.Printf("key:%v value:%v\n", key, value) return true }) // key:22 value:22 // key:11 value:11 }

其实 sync.Map 并不复杂,只是将普通 map 的相关操作转成对应函数而已。

普通 map sync.Map
map 获取某个 key   map[1]   sync.Load(1)  
map 添加元素   map[1] = 10   sync.Store(1, 10)  
map 删除一个 key   delete(map, 1)   sync.Delete(1)  
遍历 map   for...range   sync.Range()  

sync.Map 两个特有的函数,不过从字面就能理解是什么意思了。
LoadOrStore:sync.Map 存在就返回,不存在就插入
LoadAndDelete:sync.Map 获取某个 key,如果存在的话,同时删除这个 key

源码解析 type Map struct { mu Mutex read atomic.Value // readOnly read map dirty map[interface{}]*entry // dirty map misses int }

sync map 全景图

Load

Store

Delete

read map 的值是什么时间更新的 ?

Load/LoadOrStore/LoadAndDelete 时,当 misses 数量大于等于 dirty map 的元素个数时,会整体复制 dirty map 到 read map

Store/LoadOrStore 时,当 read map 中存在这个key,则更新

Delete/LoadAndDelete 时,如果 read map 中存在这个key,则设置这个值为 nil

dirty map 的值是什么时间更新的 ?

完全是一个新 key, 第一次插入 sync.Map,必先插入 dirty map

Store/LoadOrStore 时,当 read map 中不存在这个key,在 dirty map 存在这个key,则更新

Delete/LoadAndDelete 时,如果 read map 中不存在这个key,在 dirty map 存在这个key,则从 dirty map 中删除这个key

当 misses 数量大于等于 dirty map 的元素个数时,会整体复制 dirty map 到 read map,同时设置 dirty map 为 nil

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsszds.html