1. Sync.Map 的必要性
在 Go 语言中,标准的 map
类型并不是线程安全的。在多个 goroutine 并发访问时,需要使用锁来保护数据。为了解决这个问题,Go 语言在 1.9 版本中引入了 sync.Map
,它是一个并发安全的 Map 实现,可以在多个 goroutine 中安全地读写数据。
为什么Map的并发是不安全的?
2. Sync.Map 的底层原理
sync.Map
底层使用了两个原生的 Map:一个是 read
,用于读操作,另一个是 dirty
,用于写操作。其中,read
可视为“高速缓存”,当 goroutine 从 sync.Map
中读数据时,会首先查看 read
这个缓存层是否有用户需要的数据。如果有,则通过原子操作将数据读取并返回,这是 sync.Map
的快路径,也是其读性能极高的原因。
写操作直接写入 dirty
,而 读操作则先读 read
,如果没有命中,则再读 dirty
。
3. 适用场景
sync.Map
适用于以下两种场景:
- 写少读多:例如缓存,只写一次,读取多次。
- 多个 goroutine 操作不同 key:多个 goroutine 读取、写入和覆盖不相交的 key 集的条目。
因为大量写入的时候,会导致read map读不到数据而进一步加锁读取,同时dirty map也会一直晋升为read map,整体性能差,不如map + mutex
4. Sync.Map 的方法
sync.Map
提供了以下几个方法:
- Store(key, value any):向 Map 中存储键值对。
- Load(key any):根据键获取值。
- Delete(key any):删除键值对。
- LoadAndDelete(key any):获取并删除键值对。
- LoadOrStore(key, value any):如果 key 已经存在,返回对应值,如果不存在,存储键值对。
- Range(f func(key, value any) bool):遍历 Map 中的键值对。
5.代码实现
总结
sync.Map
是 Go 语言中一个非常实用的并发安全的 Map 实现,特别适用于读多写少的场景。通过其底层的读写分离机制,sync.Map
提供了高效的读性能和简洁的使用方式。然而,在大量写入的情况下,由于需要频繁更新 read
,可能会导致性能下降,因此需要根据实际场景选择使用。