只读map,对该map的访问不需要加锁,但是这个map也不会增加元素,元素会被先增加到dirty中,然后后续会迁移到read只读map中,通过原子操作所以不需要加锁操作。
entryreadOnly.m和Map.dirty存储的值类型是*entry,它包含一个指针p, 指向用户存储的value值,结构如下:
type entry struct { p unsafe.Pointer // *interface{} }p有三种值:
nil: entry已被删除了,并且m.dirty为nil
expunged: entry已被删除了,并且m.dirty不为nil,而且这个entry不存在于m.dirty中
其它: entry是一个正常的值
查找根据key来查找 value, 函数为 Load(),源码如下:
// src/sync/map.go // Load returns the value stored in the map for a key, or nil if no // value is present. // The ok result indicates whether value was found in the map. func (m *Map) Load(key interface{}) (value interface{}, ok bool) { // 首先从只读ready的map中查找,这时不需要加锁 read, _ := m.read.Load().(readOnly) e, ok := read.m[key] // 如果没有找到,并且read.amended为true,说明dirty中有新数据,从dirty中查找,开始加锁了 if !ok && read.amended { m.mu.Lock() // 加锁 // 又在 readonly 中检查一遍,因为在加锁的时候 dirty 的数据可能已经迁移到了read中 read, _ = m.read.Load().(readOnly) e, ok = read.m[key] // read 还没有找到,并且dirty中有数据 if !ok && read.amended { e, ok = m.dirty[key] //从 dirty 中查找数据 // 不管m.dirty中存不存在,都将misses + 1 // missLocked() 中满足条件后就会把m.dirty中数据迁移到m.read中 m.missLocked() } m.mu.Unlock() } if !ok { return nil, false } return e.load() }从函数可以看出,如果查询的键值正好在m.read中,不需要加锁,直接返回结果,优化了性能。
即使不在read中,经过几次miss后, m.dirty中的数据也会迁移到m.read中,这时又可以从read中查找。
所以对于更新/增加较少,加载存在的key很多的case,性能基本和无锁的map类似。
missLockerd() 迁移数据:
// src/sync/map.go func (m *Map) missLocked() { m.misses++ if m.misses < len(m.dirty) {//misses次数小于 dirty的长度,就不迁移数据,直接返回 return } m.read.Store(readOnly{m: m.dirty}) //开始迁移数据 m.dirty = nil //迁移完dirty就赋值为nil m.misses = 0 //迁移完 misses归0 } 新增和更新方法是 Store(), 更新或者新增一个 entry, 源码如下:
// src/sync/map.go // Store sets the value for a key. func (m *Map) Store(key, value interface{}) { // 直接在read中查找值,找到了,就尝试 tryStore() 更新值 read, _ := m.read.Load().(readOnly) if e, ok := read.m[key]; ok && e.tryStore(&value) { return } // m.read 中不存在 m.mu.Lock() read, _ = m.read.Load().(readOnly) if e, ok := read.m[key]; ok { if e.unexpungeLocked() { // 未被标记成删除,前面讲到entry数据结构时,里面的p值有3种。1.nil 2.expunged,这个值含义有点复杂,可以看看前面entry数据结构 3.正常值 m.dirty[key] = e // 加入到dirty里 } e.storeLocked(&value) // 更新值 } else if e, ok := m.dirty[key]; ok { // 存在于 dirty 中,直接更新 e.storeLocked(&value) } else { // 新的值 if !read.amended { // m.dirty 中没有新数据,增加到 m.dirty 中 // We're adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. m.dirtyLocked() // 从 m.read中复制未删除的数据 m.read.Store(readOnly{m: read.m, amended: true}) } m.dirty[key] = newEntry(value) //将这个entry加入到m.dirty中 } m.mu.Unlock() }操作都是先从m.read开始,不满足条件再加锁,然后操作m.dirty。
删除