Go问答 Go语言——sync.Map源码分析

w1015357065 · 2020年01月03日 · 545 次阅读

点击这里,查看更具体原理,优化点,方法源码分析等更多重要内容

简介: 众所周知,go 普通的 map 是不支持并发的,换而言之,不是线程 (goroutine) 安全的。博主是从 golang 1.4 开始使用的,那时候 map 的并发读是没有支持,但是并发写会出现脏数据。


golang 1.6 之后,并发地读写会直接 panic:

fatal error: concurrent map read and map write
package main
func main() {
    m := make(map[int]int)
    go func() {
        for {
            _ = m[1]
        }
    }()
    go func() {
        for {
            m[2] = 2
        }
    }()
    select {}
}

所以需要支持对 map 的并发读写时候,博主使用两种方法:
1、 第三方类库 concurrent-map。
2、 map 加上 sync.RWMutex 来保障线程 (goroutine) 安全的。

golang 1.9 之后,go 在 sync 包下引入了并发安全的 map,也为博主提供了第三种方法。本文重点也在此,为了时效性,本文基于 golang 1.10 源码进行分析。

sync.Map

结构体

Map

type Map struct {
    mu Mutex    //互斥锁,用于锁定dirty map

read atomic.Value //优先读map,支持原子操作,注释中有readOnly不是说read是只读,而是它的结构体。read实际上有写的操作

dirty map[interface{}]*entry // dirty是一个当前最新的map,允许读写

misses int // 主要记录read读取不到数据加锁读取read map以及dirty map的次数,当misses等于dirty的长度时,会将dirty复制到read }

readOnly

readOnly 主要用于存储,通过原子操作存储在 Map.read 中元素。

type readOnly struct {
    m       map[interface{}]*entry
    amended bool // 如果数据在dirty中但没有在read中,该值为true,作为修改标识
}

entry

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

原理


关键字: Go  源码分析

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册