同时读写 map 的问题
map 在并发情况下,只读线程是安全的同时读写线程不安全

下面来看下并发情况下读写 map 时会出现的问题:

  1. package main
  2. // 不停的对map进行写入
  3. func write(mapA map[int]int){
  4. for {
  5. mapA[1] = 1
  6. }
  7. }
  8. func main(){
  9. var mapA map[int]int = make(map[int]int)
  10. go write(mapA)
  11. // 不停的对map进行读取
  12. for {
  13. _ = mapA[1]
  14. }
  15. }
  16. 运行结果:
  17. fatal error: concurrent map read and map write
  18. 运行时输出提示:并发的 map 读写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对这种并发操作进行检查并提前发现

引入 sync.Map

并发读写时,一般的做法是加锁,但这样性能并不高,Go 在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,与 map 不同,它不是以语言原生形态提供,而是在 sync 包下的特殊结构
声明一个 sync.Map(不能使用 make 创建

  1. var scene sync.Map

sync.Map 特性:

  1. 不能使用 map 的操作方式不同,是使用 sync.Map 的方法进行调用,
    • Store 用来存储,
    • Load 用来获取,
    • Delete 用来删除
  2. 遍历:使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数回调函数的返回值是 bool 类型,需要继续迭代时,返回 true; 终止迭代时,返回 false
  3. 没有提供获取数量的方法,替代方法是遍历时自行计算数量
  4. sync.Map 为了保证并发安全,会有一些性能损失,因此在非并发情况下,使用 map

并发安全的 sync.Map 演示代码如下:

  1. func main(){
  2. var syncMap sync.Map
  3. // 设置值
  4. syncMap.Store("hebei", "beijing")
  5. syncMap.Store("hubei", "wuhan")
  6. syncMap.Store(1, "shenzhen")
  7. // 取值
  8. value1, err1 := syncMap.Load("hubei")
  9. value2, err2 := syncMap.Load("err")
  10. fmt.Println(value1, err1)
  11. fmt.Println(value2, err2)
  12. // 删除值(没有返回值)
  13. syncMap.Delete("hubei")
  14. // 遍历 sync.Map 中的键值对(只取第一对)
  15. syncMap.Range(func(key, value interface{}) bool {
  16. fmt.Println("iterate:", key, value)
  17. if key == "hebei"{
  18. return false
  19. } else {
  20. return true
  21. }
  22. })
  23. }
  24. 运行结果:
  25. wuhan true
  26. <nil> false
  27. iterate: hebei beijing


image.png

image.png

image.png


image.png