// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
// m 包含所有只读数据,不会进行任何的数据增加和删除操作
// 但是可以修改 entry 的指针(即更新 value),因为这个不会导致map的元素移动
m map[interface{}]*entry
// 标志位,如果为true则表明当前read只读map的数据不完整,dirty map中包含部分数据
amended bool // true if the dirty map contains some key not in m.
}
type entry struct {
p unsafe.Pointer // *interface{}
}
// 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) {
// 先从只读 read 的 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中存不存在,都在missLocked() 中将 misses + 1。misses = len(m.dirty)时就会把 dirty 中的数据迁到 read 中
m.missLocked()
}
m.mu.Unlock()
}
if !ok {
return nil, false
}
return e.load()
}
// 如果数据不在 read 中,经过几次 miss 后,dirty 中的数据便会迁移到 read 中,这时又可以从 read 中查找到
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
}
// src/sync/map.go
// Store sets the value for a key.
func (m *Map) Store(key, value interface{}) {
// 直接在 read 中查找值,找到了,就尝试 tryStore 更新值, tryStore 里使用 for 循环 + CAS 的方式进行更新
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 { // double check,因为 lock 前 read 里可能又有了
if e.unexpungeLocked() { // 未被标记成删除
m.dirty[key] = e // 加入到 dirty 里
}
e.storeLocked(&value) // 设置值
} else if e, ok := m.dirty[key]; ok { // 存在于 dirty 中,直接更新
e.storeLocked(&value)
} else { // read 和 dirty 中都没有,即插入新数据
if !read.amended { // if dirty 为空,即向 dirty 中第一次加载数据
m.dirtyLocked() // 会将 read 里的所有数据加载到 dirty 中,并将 read 里的键值标记为 expunged
m.read.Store(readOnly{m: read.m, amended: true}) // 将 read.amended 字段标记为true,下次查找会启用dirty查找
}
m.dirty[key] = newEntry(value) // 将这个entry加入到m.dirty中
}
m.mu.Unlock()
}
func (e *entry) tryStore(i *any) bool {
for {
p := atomic.LoadPointer(&e.p)
if p == expunged {
return false
}
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
return true
}
}
}
// 遍历 read 中的 key 并写入 dirty
// 这个效率应该很差吧
func (m *Map) dirtyLocked() {
if m.dirty != nil {
return
}
read, _ := m.read.Load().(readOnly)
m.dirty = make(map[interface{}]*entry, len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {
m.dirty[k] = e
}
}
}