由来
go的map是线程不安全的,在同时读写的时候会抛出panic。为了保证线程安全需要使用锁把他全局锁住,那么他的效率就会比较低。所以Go有一个线程安全的map——sync.map。
简述
sync.map通过读写分离的方式,在多读少写的情况,降低锁时间获得了效率的提高。简单来说就是读read,写dirty
数据结构
上代码
type Map struct {
mu Mutex
read atomic.Value
dirty map[interface{}]*entry
misses int
}
简单解释下变量
- mu 即锁,保护dirty
- read 并发读安全
- dirty 普通的map,对他进行操作时必须上锁
- misses 失效次数,影响到dirty转移到read
read的结构
type readOnly struct {
m map[interface{}]*entry
amended bool
}
可以看到read和dirty都包含entry
再看下entry的代码
type entry struct {
p unsafe.Pointer
}
是一个p指针,指向value。意味着read和dirty都是各自维护一个key,但是是指向的相同的value,value的修改对于两者是可见的,如图:
理清这里,才能去看他的增删改查的操作。
读取
- 尝试直接读取readmap
- 如果没有,上锁,doublecheck,再读一次 read map (double check的目的是防止在山所过程中dirtymap提升到readmap)
- 如果还没有,就去读 Dirty map
- 不管读不读的到,都把miss的值加1,然后判断miss有没有到达阈值,到达了说明readmap和dirtymap已经差了很多了,要把dirtymap提升成readmap
- 解锁
写入
- 先尝试直接修改read map
- 如果没有的话,上锁,double check
- 如果还没有,就去查dirty map有没有
- 如果有就改,没有就加
- 解锁
删除
- 先看read map有没有
- 有的话,那就把 value 直接改成 nil,标记删除
- 如果没有,直接删 dirty