day06-go sync.map
https://mp.weixin.qq.com/s/mXOU8TElP8bbqaybRKN8eA
go原生的map是不支持线程安全的,在对她并发操作的时候必须要加锁,
sync.map支持store,delete,LoadOrStore,range等操作,具体方法请自行测试
sync.map利用read 和 dirty 两个 map 来进行读写分离,降低锁时间来提高效率
数据结构为
type Map struct {
mu Mutex
read atomic.Value // readOnly
dirty map[interface{}]*entry
misses int
}
// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
m map[interface{}]*entry
amended bool // true if the dirty map contains some key not in m.
}
read,dirty则是真正存储k,v的结构,可以看到read是atomic.Value 可以被并发的读,而dirty是一个原始的map,它包含新写入的key以及read中所有未被删除的key,在对dirty进行load/store时需要进行加锁操作,因此他可以很快的被提升为read,而向外提供服务
read和dirty都有一个entry结构
type entry struct {
p unsafe.Pointer // *interface{}
}
可以看到read和dirty都各自维护了一个key,但是value值都指向了同一个地址
总结:
sync.map 是线程安全的,读取,插入,删除也都保持着常数级的时间复杂度。
通过读写分离,降低锁时间来提高效率,适用于读多写少的场景。
Range 操作需要提供一个函数,参数是 k,v,返回值是一个布尔值:f func(key, value interface{}) bool。
调用 Load 或 LoadOrStore 函数时,如果在 read 中没有找到 key,则会将 misses 值原子地增加 1,当 misses 增加到和 dirty 的长度相等时,会将 dirty 提升为 read。以期减少“读 miss”。
新写入的 key 会保存到 dirty 中,如果这时 dirty 为 nil,就会先新创建一个 dirty,并将 read 中未被删除的元素拷贝到 dirty。
当 dirty 为 nil 的时候,read 就代表 map 所有的数据;当 dirty 不为 nil 的时候,dirty 才代表 map 所有的数据
sync.Map适合读多写少的情况