type Map struct { mu Mutex read atomic.Value // readOnly dirty map[interface{}]*entry misses int }
1 2 3 4 5 6
// readOnly is an immutable struct stored atomically in the Map.read field. type readOnly struct { m map[interface{}]*entry // 注意这个标志,这个翻译为修改过的,如果为 true 表示 dirty 和 read 是不一致的了 amended bool// true if the dirty map contains some key not in m. }
1 2 3 4
// An entry is a slot in the map corresponding to a particular key. type entry struct { p unsafe.Pointer // *interface{} }
func(m *Map) Store(key, value interface{}) { // 先从 read 中获取,如果存在则尝试直接修改,注意这里的修改是一个原子操作,并且判断了这个 key 是否已经被删除 read, _ := m.read.Load().(readOnly) if e, ok := read.m[key]; ok && e.tryStore(&value) { return }
m.mu.Lock() // double check read, _ = m.read.Load().(readOnly) if e, ok := read.m[key]; ok { if e.unexpungeLocked() { // The entry was previously expunged, which implies that there is a // non-nil dirty map and this entry is not in it. // 这里比较难理解一点: // 如果加锁之后,read 能获取到,并且被标记为删除,则认为 dirty 肯定不为空,并且这个 key 不在 dirty 中,所以需要在 dirty 中添加这个 key m.dirty[key] = e } // 然后将 value 值更新到 entry 中 e.storeLocked(&value) } elseif e, ok := m.dirty[key]; ok { // 如果 read 中还是不存在,但是 dirty 中存在,则直接更新 e.storeLocked(&value) } else { // 如果read,dirty都没有,则是一个新的 key if !read.amended { // We're adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. // 如果之前 read 没有被修改过,则需要初始化 dirty m.dirtyLocked() // 并且需要更行 read 为 已经被修改过 m.read.Store(readOnly{m: read.m, amended: true}) } // 此时 dirty 已经被初始化过了,直接添加新的 key 就可以了 m.dirty[key] = newEntry(value) } m.mu.Unlock() }
func(e *entry) tryStore(i *interface{}) bool { for { p := atomic.LoadPointer(&e.p) // 如果这个键已经被删除了,则直接返回 if p == expunged { returnfalse } // 如果还存在,则直接修改,利用 cas 原子操作 if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { returntrue } } }
read, _ := m.read.Load().(readOnly) m.dirty = make(map[interface{}]*entry, len(read.m)) for k, e := range read.m { // 注意这里有一个细节,需要验证是否已经被删除,如果已经被删除无需要添加到 dirty 中 // 并且内部将 nil 的 entry 设置为 expunged if !e.tryExpungeLocked() { m.dirty[k] = e } } }
func(e *entry) tryExpungeLocked() (isExpunged bool) { p := atomic.LoadPointer(&e.p) for p == nil { if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { returntrue } p = atomic.LoadPointer(&e.p) } return p == expunged }
// Delete deletes the value for a key. func(m *Map) Delete(key interface{}) { m.LoadAndDelete(key) } func(m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] // 如果 read 中没有,就尝试去 dirty 中找 if !ok && read.amended { m.mu.Lock() // 这里也是 double check read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { // 反正只要 dirty 中有就直接操作就好了 e, ok = m.dirty[key] delete(m.dirty, key) // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. // 这里是细节需要增加 miss 的计数,因为这里本质也是一个 load 操作, LoadAndDelete 嘛 m.missLocked() } m.mu.Unlock() } // 如果 read 中有,就直接操作就好了 if ok { return e.delete() } returnnil, false }
// 注意这里上面的所有删除操作都是直接 cas 交换为 nil,并不是很多人说的标记为 expunged,expunged 是在 read 重新赋值个 dirty 初始化的时候才进行的 func(e *entry)delete() (value interface{}, ok bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { returnnil, false } if atomic.CompareAndSwapPointer(&e.p, p, nil) { return *(*interface{})(p), true } } }