前面讲到,Go中Map不是并发安全的(体现为操作前需要读flag),而Go1.19开始引入了sync.Map,这是一种同步的Map,是并发安全的。
用法
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
//增加,对应于java的put
m.Store("xiaoming", 19)
m.Store("xiaohong", 30)
//查,对应于get
age, _ := m.Load("xiaoming")
fmt.Println(age.(int))
//对应于getOrDefault()
m.LoadOrStore("lan", 100)
ages, _ := m.Load("lan")
fmt.Println(ages)
//遍历
m.Range(func(key, value interface{}) bool {
name := key.(string)
age := value.(int)
fmt.Println(name, age)
return true
})
//删除,对应于remove()
m.Delete("xiaoming")
age, ok := m.Load("xiaoming")
fmt.Println(age, ok)
}
使用场景
- 原生的map使用的时候需要加锁,不过一般情况下出于对类型安全的考虑,原生的就够用了。sync包下的map不用加锁,而是通过信号量mutex。两种情况下用它比较多:
- 一次写入,多次读取
- 多个协程读/写/覆盖不相交的键值集合
底层实现
type Map struct {
mu Mutex
//注意如果要更新一个之前已被删除的entry,需先将其状态改为nil,再复制到dirty中,再更新
//不加锁,保证原子性
read atomic.Value // readOnly
//类型是原始的map,包含新写入的key和read中所有未被删除的key,可快速将dirty提升为read对外服务
//加锁
dirty map[any]*entry
//每次读取read失败,misses值+1,到达阈值后提升dirty为read
misses int
}