Go语言map的详解(第二篇)之并发安全的sync.Map

Go语言普通map不是线程安全的,无法应用于Go语言的高并发场景。Go语言原生实现了一种线程安全的sync.Map,可以实现并发的读写,并且性能比map+锁的机制的性能要高许多。

1.sync.Map的底层结构

在sync的包下含有一个结构体Map:

底层数据
在Map的结构体中含有4个字段,mu 是一个互斥锁,read是一个只读的结构体,如下图所示,该结构体中含有一个普通的map以及一个amended布尔值,用来表征dirty map中是否有read map中没有的数据,如果为true说明dirty map中含有readmap中没有的数据,如果为false则说明dirt map中没有read map中没有的数据。
在这里插入图片描述
Map的结构体中有一个dirty字段,该字段是一个普通的map,还有一个misses 整形,表示的是在read的map中查询数据的没有命中的次数。

最开始的时候sync.Map的结构如下,当中存有两个k-v,存储的结构如下。dirtmap和readmap的value都是一个万能指针,指向的是key对应的数据。
结构

2.sync.Map解决map的并发安全问题

(1)读操作和修改1:
在进行读操作的时候,会默认先查read字段,去read map中查询,如果查询到则返回。例如,查询key = “a”的值,会往read map中查询,查询到了,则直接返回“A”。同理如果要修改key = “a”的值,先查询read map,查到了然后进行修改。
在这里插入图片描述
(2)append一个k-v

假设现在要加入一个k-v,key = ”c“,value = ”C“。

首先会先检查read map,如果read map中已经存在该键值,则直接修改该键值的value。
如果没有找到该键值,说明要进行追加,会先将amended 置为 true,表示将有dirty中将会添加入read map中没有的元素。
然后获取互斥锁字段mu,并对dirty map进行上锁,然后再操作dirty map,将新的元素加入其中。
在这里插入图片描述
(3)读或者修改2:
如果此时我们需要读key = “c”,首先会先去read map中查找,如果再read map中没有找到,然后查看amended字段,如果amended 为false则,不会去查找dirty map,返回。,如果amended字段为true表明dirty map中含有read map中没有的数据。所以会往dirty map中寻找,读完或修改后将misses字段加1,表示raad map查找失败次数增加1。
在这里插入图片描述
(4)dirty map提升
当misses = len(dirty)的时候,也就是在进行查询操作的时候有len(dirty)次在read map长没查找到。一直读取dirty 是违背我们高并发sync.Map的初衷的。解决的办法是,将下面的dirty map提升,替换掉原来的read map。将dirty map提升替换掉read map,然后将amended字段改为false,并且将misses改为0,提升后一开始的dirty map是nil,之后要追加的时候再重建dirty map。

在这里插入图片描述
再往dirty map 中追加元素的时候,重构dirty map。

(4)正常删除
先查询read map,如果找到了,则将万能指针Pointer置为nil,然后Go语言的垃圾回收会自动的将value回收。
例如要删除key = “c”。
在这里插入图片描述
(5)追加后删除

要删除的key在read map中没有只能去dirtymap中删除。首先先去read map中查询,没找到,然后看amended字段,如果为true,则获取互斥锁mu,并对dirty map加锁,加锁后去dirty map中查找,找到了就将key对应的value置为nil。

在这里插入图片描述
追加后删除,假设现在需要将dirty map提升,则提升后,原来删除的key=“d”的value的指针指向的是一个expunged字段,表明的是,该键已经删除,在后序重构dirty的时候可以不重构它。
重构dirty map的时候并不加上删除字段。
在这里插入图片描述

3.总结

sync.Map底层使用了两个map分离了map的扩容问题,分别是read map 和dirty map,read map主要用于对key的查找和修改,不会引发扩容的操作,dirty map主要用于新增数据,可能会引发扩容,这时候需要加锁。

优缺点:

优点: 使用两个map将进行读写和追加分离,线程安全的,在读多追加少的场景具有较高的性能。
缺点: 不适用于大量追加的场景,在追加的时候在read map中查找不到数据的时候,会进行加锁对dirty map进行操作,并进行提升和重构。但是,在 dirty map 刚被提升后,将 read map 复制到新的 dirty map中,在存在大量的数据的情况下复制操作会阻塞所有的协程极大的影响性能

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值