/ Go 语言 sync.Map 用法详解 /
Go 语言中的 map 在并发操作时不是线程安全的,为了解决这个问题,Go 1.9 版本添加了 sync.Map 这种并发安全的 map 实现。正确使用 sync.Map 可以有效防止并发环境下的数据竞争问题。
本文将详细介绍 sync.Map 的相关用法,内容涵盖:
- sync.Map 背景
- sync.Map 接口介绍
- sync.Map 的存储读取删除
- sync.Map 遍历
- sync.Map 与普通 map 的区别
- sync.Map 使用注意事项
- sync.Map 应用示例
- sync.Map 原理简析
通过本文可以全面了解 sync.Map 的工作原理及使用技巧,用来构建高效的并发程序。
1
1. sync.Map 背景
Go 语言中的 map 在多线程并发使用时不是线程安全的,主要问题是读写 map 时会存在数据竞争,导致错误。
例如并发读写 map:
var m = map[string]int{} go func() { for { m["count"]++ } }() go func() { for { _ = m["count"] } }()
这样会引发 fatal error: concurrent map read and map write。
为了解决这个问题,在 Go 1.9 中添加了 sync.Map 做为并发安全的 map 实现。
2
2. sync.Map 接口
sync.Map 的接口实现了一个自动并发安全的 map,主要方法有:
func New() *Map // 创建一个空的Map m.Store(key, value) // 存储key-value m.Load(key) // 获取key对应的value m.LoadOrStore(key, value) // 获取或存储并返回值 m.Delete(key) // 删除key m.Range(f func(key, value interface{})) // 范围遍历
这些方法都天生具备并发安全访问能力,使用 sync.Map 就不会有并发数据竞争的风险。
我们可以直接通过 sync.Map 来并发安全地使用一个 map:
var m sync.Map m.Store("count", 0) // 并发安全地读写map
3
3. 存储和读取
sync.Map 提供了 Store 和 Load 方法进行数据存储和读取:
m.Store(key, value) // 存储 value, ok := m.Load(key) // 读取 if !ok { // key不存在 } else { // 获取value }
Store 会将 key-value 放入 sync.Map 中存储。
Load 方法可以读取 key 对应的 value,它返回值和一个 bool 类型表示是否读取成功。
同时提供了 LoadOrStore 方法,可以读取或存储:
actual, loaded = m.LoadOrStore(key, value)
它可以获取 key 对应的 value,如果 key 不存在就保存这个 value 并返回。
4
4. 删除元素
可以使用 Delete 方法来删除元素:
m.Delete(key)
删除成功后,再 Load 这个 key 会返回 value 类型的默认零值。
和普通 map 类似,删除不存在的 key 不会报错。
5
5. 遍历
sync.Map 提供了 Range 方法用于遍历所有键值对:
m.Range(func(k, v interface{}) bool { // 在此处理 kv return true })
遍历是无序的,和 map 的迭代顺序一致。
返回 true 会继续迭代,false 会停止遍历。
6
6. 与普通 map 区别
sync.Map 和普通的 map 有以下区别:
- sync.Map 设计为并发安全,不需要额外同步
- 没有容量概念,扩容自动处理
- 只提供了常用的 map 操作方法
- 不保证遍历顺序
- 没有直接转换为普通 map 的方法
在需要并发安全时选择 sync.Map,其他情况普通 map 更加高效。
7
7. 使用注意事项
使用 sync.Map 有以下注意事项:
- 指针值作为 key 需要小心
- 同时读写 key 会产生不确定行为
- 删除时内存不能及时释放
- 遍历时需要复制值进行处理
需要针对这些特点合理安全地使用。
8
8. 应用示例
sync.Map 常见使用场景:
- 缓存
- 计数器
- 生产消费队列等
实现一个简单计数器:
var counter = sync.Map{} func inc(key string) { counter.Store(key, 0) v := counter.Load(key) counter.Store(key, v+1) } // 并发安全计数
9
9. sync.Map 原理简析
sync.Map 内部通过锁分段实现并发安全:
它使用一个包含多把锁的结构,每一把锁锁定一个 map 元素区间。
当访问元素时找到锁定的段,对该段加锁来保证安全:
segment1 map[1] segment2 map[2] segment3 map[3] | | | lock1 lock2 lock3
这样实现多段锁,既保证了线程安全,又提高了效率。
10
总结
通过这篇文章,我们全面介绍了 sync.Map 的工作原理和使用技巧,它可以有效解决 Go 语言中 map 并发安全的问题。但也需要注意它和普通 map 的区别,合理使用。