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.Mapmap 获取某个 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
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