深入浅出 Go - sync.Map 源码分析

Go 的 map 在并发场景下,只读是线程安全的,读写则是线程不安全的。Go1.9 提供了并发安全的 sync.Map,通过阅读源码我们知道 snyc.Map 通过读写分离的机制,降低了锁的粒度,以此来提高并发性能
并发不安全的 map
执行程序会报如下错误
并发安全的 sync.Map
对于并发不安全的 map,一般这种情况我们可以通过加锁的方式来实现并发安全的 hashmap,但是锁本身也会带来额外的性能开销,所以 Go1.9 开始标准库提供了并发安全的 sync.Map,使用起来也很简单,如下
sync.Map
直接进入主题,看源码
read只读数据readOnlydirty读写数据,操作dirty需要用mu进行加锁来保证并发安全misses用于统计有多少次读取read没有命中amended用于标记read和dirty的数据是否一致
Load
从 Load 的源码可以看出读取数据时,首先是从 read 读,没有命中的话会到 dirty 读取数据,同时调用 missLocked() 增加 misses
可以看到当 misses 大于 len(dirty) 时则表示 read 和 dirty 的数据相差太大,sync.Map 会将 dirty 的数据赋值给 read,而 dirty 会被置空
Store
Store 首先会直接到 read 修改数据,修改成功则直接返回,如果 key 不存在那么就表示要到 dirty 找数据,如果 dirty 存在 key 则修改,如果不存在则新增,同时还要将 read 中的 amended 标记为 true,表示 read 和 dirty 的数据已经不一致了
Range
Range 的源码没太多可以说的,有两点需要关注,一个是 Range 会保证 read 和 dirty 是数据同步的,另一个是回调函数返回 false 会导致迭代中断
Delete
Delete 采用的是延迟删除的机制,首先会到 read 查找是否存在 key,如果存在则执行 entry.delete 进行软删除,通过 CAS 将指针 entry.p (存放数据的指针) 置为 nil,减少锁开销提高并发性能。只有当 read 找不到 key 且 amended 为 true 才会通过 delete 进行硬删除,记住这个阶段是会加锁的

版权声明: 本文为 InfoQ 作者【哈希说】的原创文章。
原文链接:【http://xie.infoq.cn/article/ebcb070ee7fd0e273ca53b64f】。文章转载请联系作者。











评论