go sync.map实现

bunnyhuangw · · 939 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang map是非goroutine安全,如果多个goroutine使用map需要加锁。但在高并发场景下,锁的争用会造成系统性能的下降。为了解决这种问题,go 1.9之后提供了线程安全:sync.map。sync.map引入了两个数据结构read,dirty来存储,他们的底层都是用map来实现。

Golang选择了 CAS 这种不用加锁的方案来更新值,实现并发安全的map。

下面例举了三个结构体

map: sync.map的结构体,包含read和dirty,read和dirty存储了map中真实存储的key/value值。misses表示从dirty读取值的次数
readonly:Map.read值的结构体类型,m存储key/value真实值。readOnly.amended表示read是否创建了dirty副本
entry: read和dirty中存储value的指针

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

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

type entry struct {
    p unsafe.Pointer // *interface{}
}

read是readOnly结构体,真实数据存储在readOnly.m。

read和dirty的关联:
image.png

1: read相当于cache,读取数据时,先从read读取,没有取到,从dirty读取,Map.misses++。
当Map.misses达到dirty长度时,把dirty里面的数据全部copy到read中,并且dirty置为nil。

2: read和dirty map存储的元素值是放在entry结构体中。read和dirty中相同key值指向同一个entry地址,所以当对read的key对应的value值进行修改,dirty中的值也会相应的被修改。

entry.p 的状态:
1: nil表示entry被删除,并且Map.dirty = nil
2: expunged(初始化的entry.p)表示entry被删除,但是Map.dirty != nil
3: 其他情况表示值存在

snyc.Map主要提供了插入,查找,删除操作,接下来会主要会讲这三个方法的实现

插入流程
插入key, value
1: 先从read中获取key,如果存在,并且这个key没有被删除,则直接更新read[key] = entry{p: value}返回
2: 否则,key存在但是被删除了,在dirty中插入这个key,value值。dirty[key] = entry{p: value}返回
3: 如果dirty为nil,则将read map的key,entry 添加到新创建的dirty map中;不为nil,则跳过第3步
4: 将key, value插入dirty map中。dirty[key] = entry{p: value}

插入总结:
新加入的key值,会插入dirty中
以前存在,但是删除过的key,会插入dirty中
以前存在,但是没被删除的key,read会更新这个key对应的value值,
所以 dirty不为nil的时候,会全量保存key值。

查找流程
查找key
1: 从read中读取到,直接返回
2: 没有读取到,并且dirty不为nil,对map加锁,然后再读取一遍read map中内容(主要防止在加锁的过程中,有可能dirty map全部copy到read map,dirty置为nil),如果read存在key,直接返回
3: read不存在,从dirty中读取key对应的value值返回,并且map.misses++。当map.misses达到一定dirty长度,将dirty map全部copy到read map,dirty置为nil。

查找总结:
读read没读到,会从dirty中读取,并且misses 次数+1,当次数达到一定dirty长度,会把dirty map全部copy到read map,dirty置为nil。

删除流程
1: 从read中去读key,如果存在,直接将从read[key]获取到entry.p 置为nil
2: 否则,从dirty中删除这个key值
所以可以得出,read删除是直接把entry的p置为nil,key保留。从dirty中删除是直接删除这个key


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:bunnyhuangw

查看原文:go sync.map实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

939 次点击  ∙  1 赞  
加入收藏 微博
1 回复  |  直到 2019-10-25 09:56:47
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传