深入理解golang:sync.map (2)

只读map,对该map的访问不需要加锁,但是这个map也不会增加元素,元素会被先增加到dirty中,然后后续会迁移到read只读map中,通过原子操作所以不需要加锁操作。

entry

readOnly.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。

删除

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffdg.html