GO 语言中的 sync Map

为什么需要 sync map

go 语言之所以引入 sync.Map主要是因为GO 语言自带的 map 是线程不安全的。只能保证并发的读,但是不能保证并发的写。

看下面的例子:

func main() {
	m := make(map[int]int)
	go func() {
		for {
			m[1] = 1 // 并发的写
		}
	}()
	go func() {
		for {
			_ = m[1] //并发的读
		}
	}()
	time.Sleep(time.Hour)
}

运行结果如下

注意:

  1. 出现这种现象的原因,主要是因为:错误信息显示,并发的 map 读和 map 写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对这种并发操作进行检查并提前发现。

  2. 其实,就算你在 第一个协程和第二个协程中间,加上 time.Sleep(time.Second) ,依然也会报 panic

使用 sync.map

代码如下,再并发的读写,就不会报错了。

func main() {
	var m sync.Map
	go func() {
		m.Store(1, "小张")
	}()
	time.Sleep(time.Second)
	go func() {
		v, ok := m.Load(1)
		if ok {
			fmt.Println(v.(string))
		}
	}()
	time.Sleep(time.Second)
}

如果想把 sync.map 中的 key 和 value 遍历出来,需要用 sync.map 自带的特殊方法

func main() {
	var m sync.Map
	go func() {
		m.Store(1, "小张")
	}()
	time.Sleep(time.Second)
	go func() {
		v, ok := m.Load(1)
		if ok {
			fmt.Println(v.(string))
		}
	}()
	time.Sleep(time.Second)

	// 遍历 sync.map 中的 key,val
	m.Range(func(key, value interface{}) bool {
		fmt.Println("key:", key.(int), "value", value.(string))
		if key.(int) == 2 {
			return false
		}
		return true
	})
}

// 输出结果如下
小张
key: 1 value 小张
key: 2 value 小王

注意:

  1. sync.map 无须初始化,直接声明即可。

  2. sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。

  3. 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。

  4. sync.Map 为了保证并发安全有一些性能损失,因此在非并发情况下,使用 map 相比使用 sync.Map 会有更好的性能。

posted @ 2022-01-20 01:48  沧海一声笑rush  阅读(429)  评论(0编辑  收藏  举报