Golang - sync.map 设计思想和底层源码分析

本文详细分析了Go语言中的并发安全map `sync.map`,探讨了其设计思想,包括空间换时间、读写分离、双检查机制等,并深入源码解析了查找、添加、删除和遍历操作,适合于读多写少的并发场景。
摘要由CSDN通过智能技术生成

Golang - sync.map 设计思想和底层源码分析

一.引言

  • 在Go v1.6之前,内置map是部分goroutine安全的,并发读没有问题,并发写可能有问题

  • 在Go v1.6之后,并发读写内置map会报错,在一些知名的开源库都有这个问题,所以在Go v1.9之前,解决方案是加一个额外的大锁,锁住map。

  • 在Go v1.9中,go官方提供了并发安全的map,sync.map。

本文Go版本:v1.14.4

二. sync.map的设计思想

在map内数据非常大的时候,采用一个大锁,会使得锁的竞争十分激烈,存在性能问题

  • Java内的解决方案是分段锁机制,比如ConcurrentHashMap,内部使用多个锁,每个区间共用一把锁,这样锁的粒度更小了,减少了数据共享一把大锁带来的性能影响

但是由于其实现的复杂性和其他因素,Go官方并没有采用上述方案,而是另辟蹊径,采用读写分离的形式,来实现了一个并发安全的map

后续笔者会考虑自己实现一个分段锁机制的map,然后和sync.map进行一下比较,观察在不同场景下的性能差异,敬请期待

1. 空间换时间

如果采用传统的大锁方案,其锁的竞争十分激烈,也就意味着需要花在锁上的时间很多,我们要尽可能的减少时间消耗,针对耗时太长的情况,算法中有一种常见的解决方案,空间换时间,采用冗余的数据结构,来减少时间的消耗。

sync.map中冗余的数据结构就是dirty和read,二者存放的都是key-entry,entry其实是一个指针,指向value,read和dirty各自维护一套key,key指向的都是同一个value,也就是说,只要修改了这个entry,对read和dirty都是可见的

那空间换时间策略在sync.map中到底是如何体现的呢?到底在哪些地方减少了耗时?

  • 遍历操作:只需遍历read即可,而read是并发读安全的,没有锁,相比于加锁方案,性能大为提升
  • 查找操作:先在read中查找,read中找不到再去dirty中找

核心思想就是一切操作先去read中执行,因为read是并发读安全的,无需锁,实在在read中找不到,再去dirty中。read在sycn.map中是一种冗余的数据结构,因为read和dirty中数据有很大一部分是重复的,而且二者还会进行数据同步

2.读写分离

sync.map中有专门用于读的数据结构:read,将其和写操作分离开来,可以避免读写冲突

而采用读写分离策略的代价就是冗余的数据结构,其实还是空间换时间的思想。

3.双检查机制

通过额外的一次检查操作,来避免在第一次检查操作完成后,其他的操作使得检查条件产生突然符合要求的可能。

在sync.map中,每次当read不符合要求要去操作dirty前,都会上锁,上锁后再次判断是否符合要求,因为read有可能在上锁期间,产生了变化,突然又符合要求了,read符合要求了,尽量还是在read中操作,因为read并发读安全。

4. 延迟删除

在删除操作中,删除kv,仅仅只是先将需要删除的kv打一个标记,这样可以尽快的让delete操作先返回,减少耗时,在后面提升dirty时,再一次性的删除需要删除的kv

5.read优先

需要进行读取,删除,更新操作时,优先操作read,因为read无锁的,更快,实在在read中得不到结果,再去dirty中

read的修改操作需要加锁,read只是并发读安全,并发写并不安全

6. 状态机机制

entry的指针p,是有状态的,nil,expunged(指向被删除的元素),正常,三种状态.
那其状态在sync.map各个操作间又是怎么变化的呢?

主要是两个操作会引起p状态的变化:Store(新增/修改) 和 删除

我们先来看看第一个操作 Store(新增/修改)

  • 在Store更新时,如果key在read中存在,并且被标记为已删除,会将kv加入dirty,此时read中key的p指向的是expunged,经过unexpungeLocked函数,read中的key的p指向会从expunged改为nil࿰
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值