Go sync.map的理解分析

249 阅读2分钟

由来

go的map是线程不安全的,在同时读写的时候会抛出panic。为了保证线程安全需要使用锁把他全局锁住,那么他的效率就会比较低。所以Go有一个线程安全的map——sync.map。

简述

sync.map通过读写分离的方式,在多读少写的情况,降低锁时间获得了效率的提高。简单来说就是读read,写dirty

数据结构

上代码

type Map struct {
	mu Mutex
	read atomic.Value
	dirty map[interface{}]*entry
	misses int
}	

简单解释下变量

  • mu 即锁,保护dirty
  • read 并发读安全
  • dirty 普通的map,对他进行操作时必须上锁
  • misses 失效次数,影响到dirty转移到read

read的结构

type readOnly struct {
	m       map[interface{}]*entry
	amended bool 
}

可以看到read和dirty都包含entry

再看下entry的代码

type entry struct {
	p unsafe.Pointer 
}

是一个p指针,指向value。意味着read和dirty都是各自维护一个key,但是是指向的相同的value,value的修改对于两者是可见的,如图:

sync.map 整体结构

理清这里,才能去看他的增删改查的操作。

读取

  1. 尝试直接读取readmap
  2. 如果没有,上锁,doublecheck,再读一次 read map (double check的目的是防止在山所过程中dirtymap提升到readmap)
  3. 如果还没有,就去读 Dirty map
  4. 不管读不读的到,都把miss的值加1,然后判断miss有没有到达阈值,到达了说明readmap和dirtymap已经差了很多了,要把dirtymap提升成readmap
  5. 解锁

写入

  1. 先尝试直接修改read map
  2. 如果没有的话,上锁,double check
  3. 如果还没有,就去查dirty map有没有
  4. 如果有就改,没有就加
  5. 解锁

删除

  1. 先看read map有没有
  2. 有的话,那就把 value 直接改成 nil,标记删除
  3. 如果没有,直接删 dirty