20

根据拓扑图理解golang的sync.Map工作原理

 4 years ago
source link: https://studygolang.com/articles/24537
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

sync.Map的拓扑关系图

1460000020946992

读懂关系图需要了解的几个关键点

var expunged = unsafe.Pointer(new(interface{}))
type Map struct 
type readOnly struct
type entry struct
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) Store(key, value interface{})
func (m *Map) Delete(key interface{})
func (m *Map) Range(f func(key, value interface{}) bool)

这里要重点关注 readOnly.amendedMap.missesentry.p 的数值状态, 拓扑图中,多处用于走势判断.

接下来详细列出结构体的代码和注释, 方便阅读理解拓扑图.

sync.Map主要结构和注释

type Map struct {
    //互斥锁,用于锁定dirty map
    mu Mutex    
    
    //优先读map,支持原子操作,注释中有readOnly不是说read是只读,而是它的结构体。read实际上有写的操作
    read atomic.Value 
    
    // dirty是一个当前最新的map,允许读写
    dirty map[interface{}]*entry 
    
    // 主要记录read读取不到数据加锁读取read map以及dirty map的次数,当misses等于dirty的长度时,会将dirty复制到read
    misses int 
}

// readOnly 主要用于存储,通过原子操作存储在 Map.read 中元素。
type readOnly struct {
    // read的map, 用于存储所有read数据
    m       map[interface{}]*entry
    
    // 如果数据在dirty中但没有在read中,该值为true,作为修改标识
    amended bool 
}

// entry 为 Map.dirty 的具体map值
type entry struct {
    // nil: 表示为被删除,调用Delete()可以将read map中的元素置为nil
    // expunged: 也是表示被删除,但是该键只在read而没有在dirty中,这种情况出现在将read复制到dirty中,即复制的过程会先将nil标记为expunged,然后不将其复制到dirty
    //  其他: 表示存着真正的数据
    p unsafe.Pointer // *interface{}
}

sync.Map的原理很简单,使用了空间换时间策略,通过冗余的两个数据结构(read、dirty),实现加锁对性能的影响。

通过引入两个map将读写分离到不同的map,其中read map提供并发读和已存元素原子写,而dirty map则负责读写。

这样read map就可以在不加锁的情况下进行并发读取,当read map中没有读取到值时,再加锁进行后续读取,并累加未命中数。

当未命中数大于等于dirty map长度,将dirty map上升为read map。

从结构体的定义可以发现,虽然引入了两个map,但是底层数据存储的是指针,指向的是同一份值。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK