同时读写 map 的问题
map 在并发情况下,只读线程是安全的,同时读写线程不安全
下面来看下并发情况下读写 map 时会出现的问题:
package main// 不停的对map进行写入func write(mapA map[int]int){for {mapA[1] = 1}}func main(){var mapA map[int]int = make(map[int]int)go write(mapA)// 不停的对map进行读取for {_ = mapA[1]}}运行结果:fatal error: concurrent map read and map write运行时输出提示:并发的 map 读写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对这种并发操作进行检查并提前发现
引入 sync.Map
并发读写时,一般的做法是加锁,但这样性能并不高,Go 在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,与 map 不同,它不是以语言原生形态提供,而是在 sync 包下的特殊结构
声明一个 sync.Map(不能使用 make 创建
var scene sync.Map
sync.Map 特性:
- 不能使用 map 的操作方式不同,是使用 sync.Map 的方法进行调用,
- Store 用来存储,
- Load 用来获取,
- Delete 用来删除
- 遍历:使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数回调函数的返回值是 bool 类型,需要继续迭代时,返回 true; 终止迭代时,返回 false
- 没有提供获取数量的方法,替代方法是遍历时自行计算数量
- sync.Map 为了保证并发安全,会有一些性能损失,因此在非并发情况下,使用 map
并发安全的 sync.Map 演示代码如下:
func main(){var syncMap sync.Map// 设置值syncMap.Store("hebei", "beijing")syncMap.Store("hubei", "wuhan")syncMap.Store(1, "shenzhen")// 取值value1, err1 := syncMap.Load("hubei")value2, err2 := syncMap.Load("err")fmt.Println(value1, err1)fmt.Println(value2, err2)// 删除值(没有返回值)syncMap.Delete("hubei")// 遍历 sync.Map 中的键值对(只取第一对)syncMap.Range(func(key, value interface{}) bool {fmt.Println("iterate:", key, value)if key == "hebei"{return false} else {return true}})}运行结果:wuhan true<nil> falseiterate: hebei beijing
增
读
删
查
