Goland sync.Map大白话解析
Goland sync.Map大白话解析
代码解析链接:https://mp.weixin.qq.com/s/H5HDrwhxZ_4v6Vf5xXUsIg
建议对照参考链接代码食用
结构体
可以简单理解为:sync包中的Map结构体里面有两个map,分别是read和dirty,read和dirty的在结构上的最大不同点,就是read在dirty的基础上多了一个amended字段,用来表示dirty中是否存在read没有的数据。
其中read和dirty中的value值都是一个entry结构体,结构体中存放着指向该值的指针pointer,pointer有三种值,分别是nil,expunged,真正指向值的指针。nil是真正删除了,expunged是软删除。
另外还有一个misses字段和互斥锁mu,misses表示穿透了read直接命中dirty的次数。
总结:
sync.Map其实是把数据分成了读和写两个区域,从而减少了每次获取都要加锁的额外开销。
函数介绍
Load方法
Load(key interface{}) (value interface{}, ok bool) 获取Key值,返回对应的value和value存在与否
- 读readMap,如果读到了直接返回结果
- 获取不到,判断readMap中amended字段(用来表示dirtyMap中是否含有readMap没有的数据),如果amended为true,说明dirtyMap中含有readMap没有的数据,如果为false,说明不存在此数据,返回nil和false
- 加锁(全局锁),再读readMap(双重校验),读到了entry就返回结果
- 再次就那些2的判断,如果为false,说明真的没有数据,返回nil和false。如果为true,再去读dirtyMap
- 从dirtyMap获取entry,获取不到,返回nil和false,获取到了则返回对应的value和true,并且会对misses字段进行+1操作,如果misses字段大于等于dirtyMap长度,则把dirtyMap置换为read的map(相当于把dirtyMap赋值给readMap),并且重置dirtyMap,把misses设置为0,把amended字段设置为false,表示dirtyMap中不存在readMap没有的数据
- (上述的返回数据只是用临时变量去存放数据,并没有真正返回,最后才真正返回)释放锁,返回数据。
总结:
- 先读readMap,获取不到再加锁。然后双重校验再次读readMap,读不到再去访问dirtyMap。
- 访问readMap不存在但dirtyMap存在的数据,会带来加锁的额外开销。
Store方法
- 读readMap,如果读到了entry,并且值的指针不是expunged(软删除),则更新值,返回数据
- 如果读不到entry,或者值已经被软删除,则加锁,再次读readMap,双重校验。
- 如果在readMap读到了entry,并且值已经被软删除,则把entry.p的expunged替换为nil,并且在dirtyMap中添加此key和entry,然后更新entry的值,释放锁,返回。
- 如果在readMap读不到entry,则去读dirtyMap,如果在dirtyMap中读到了entry,则执行更新值的操作,并释放锁返回。
- 如果dirtyMap中也不存在此值,并且readMap的amended字段为false(dirtyMap中不含有readMap没有的数据),(如果dirtyMap等于nil,则把readMap不为expunged和不为nil的元素添加到dirty中),把amended设置为true,因为现在dirtyMap中有readMap不存在的数据,把新值添加到dirty中,释放锁,返回。
- 如果dirtyMap不存在此值,并且amended为true,则把新值添加到dirtyMap中,释放锁,返回。相比于步骤5,减少了把readMap中entry.p != expunged&&entry.p != nil的元素添加到dirtyMap中的步骤。
总结:
- Store方法优先无锁访问readMap,未命中会加锁访问dirtyMap
- 加锁访问后,会把新的元素添加到dirtyMap中,并把readMap的ammend元素设置为true,用Load函数去获取该元素,会导致加锁访问dirtyMap。并且只有到了未命中次数等于dirtyMap长度以后(Load和Delete方法都会有此检测),才会把dirtyMap升级为readMap,此后Load函数才会直接访问readMap
- 所以说,sync.Map不适合频繁插入新元素的场景,这会导致频繁加锁访问dirtyMap,带来额外的性能开销。
Delete方法
- 读readMap,如果读不到entry并且amended为true(说明dirtyMap存在),加锁,再次读readMap,双重校验,如果还是读不到entry并且amended为true,则去读dirtyMap,把dirtyMap中存在的值删掉,misses字段加一,如果misses字段大于等于dirtyMap长度,把dirtyMap升级为readMap,dirtyMap设为nil,miss设为0,返回
- 读readMap,如果读到了entry或者amended为false,如果entry.p为nil或者expunged,则直接返回,否则把entry.p设为expunged(软删除),返回。
总结:
- 删除readOnly中存在的key,可以不用加锁
- 如果删除readOnly中不存在的或者Map中不存在的key,都需要加锁
Goland sync.Map大白话解析的更多相关文章
- go sync.map源码解析
go中的map是并发不安全的,同时多个协程读取不会出现问题,但是多个协程 同时读写就会出现 fatal error:concurrent map read and map write的错误.通用的解决 ...
- go的sync.Map
sync.Map这个数据结构是线程安全的(基本类型Map结构体在并发读写时会panic严重错误),它填补了Map线程不安全的缺陷,不过最好只在需要的情况下使用.它一般用于并发模型中对同一类map结构体 ...
- 源码解读 Golang 的 sync.Map 实现原理
简介 Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent ...
- 看过这篇剖析,你还不懂 Go sync.Map 吗?
hi, 大家好,我是 haohongfan. 本篇文章会从使用方式和原码角度剖析 sync.Map.不过不管是日常开发还是开源项目中,好像 sync.Map 并没有得到很好的利用,大家还是习惯使用 M ...
- Go 1.9 sync.Map揭秘
Go 1.9 sync.Map揭秘 目录 [−] 有并发问题的map Go 1.9之前的解决方案 sync.Map Load Store Delete Range sync.Map的性能 其它 在Go ...
- Golang:sync.Map
由于map在gorountine 上不是安全的,所以在大量并发读写的时候,会出现错误. 在1.9版的时候golang推出了sync.Map. sync.Map 通过阅读源码我们发现sync.Map是通 ...
- sync.Map(在并发环境中使用的map)
sync.Map 有以下特性: 需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是 ...
- sync.Map与Concurrent Map
1. sync.Map 1.1. map并发不安全 go1.6以后map有了并发的安全检查,所以如果在并发环境中读写map就会报错 func unsafeMap() { // 创建一个map对象 m ...
- golang 标准库 sync.Map 中 nil 和 expunge 区别
本文不是 sync.Map 源码详细解读,而是聚焦 entry 的不同状态,特别是 nil 状态和 expunge 状态的区分. entry 是 sync.Map 存放值的结构体,其值有三种,分别为 ...
随机推荐
- iGear 用了这个小魔法,模型训练速度提升 300%
一个高精度AI模型离不开大量的优质数据集,这些数据集往往由标注结果文件和海量的图片组成.在数据量比较大的情况下,模型训练周期也会相应加长.那么有什么加快训练速度的好方法呢? 壕气的老板第一时间想到的通 ...
- Ubuntu Typora安装
Ubuntn Typora安装 现在好像开始收费了,网上给的方法都是从官网下载的,感觉迟早不能用,因为要钱了 官网好像给的安装方法如下,可是好像不太好用 # or run: # sudo apt-ke ...
- 洛谷P5019 [NOIP2018 提高组] 铺设道路
题目描述 春春是一名道路工程师,负责铺设一条长度为 n 的道路. 铺设道路的主要工作是填平下陷的地表.整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 di. 春春每天可以 ...
- qiankun 2.x 运行时沙箱 源码分析
简介 从源码层面详细讲解了 qiankun 框架中的 JS 沙箱 和 样式沙箱的实现原理. 序言 沙箱 这个词想必大家应该不陌生,即使陌生,读完这篇文章也就不那么陌生了 沙箱 (Sandboxie) ...
- ApacheCN Python 译文集(二)20211110 更新
Python 应用计算思维 零.序言 第一部分:计算思维导论 一.计算机科学基础 二.计算思维要素 三.理解算法和算法思维 四.理解逻辑推理 五.探究性问题分析 六.设计解决方案和解决流程 七.识别解 ...
- 「SDWC2018 Day1」网格
题目当中有三条限制,我们来逐一考虑.对于第一条限制,每次走动的增加量 \(x_i \le M_x, y_i \le M_y\),可以发现一共走的步数是确定的,那么就相当于解这样两个方程组: \[x_1 ...
- 记录常见的问题:encodeURICompnent 解码过程中出现空格 以及 第三方app中使用schema 唤起app
window.location.href 跳转的时候使用了encodeURIComponent编码了部分参数,但是在第三方app中出现了编码过后的参数换行和空格的情况(部分第三方应用或者java程序) ...
- java多线程编程(二)
1. wait 和 sleep 区别? 1.wait可以指定时间也可以不指定,sleep必须指定时间. 2.在同步中时,对cpu的执行权和锁的处理不同. wait:释放执行权,释放锁. sleep ...
- Java基础复习(七)
一.基本语法 1. java没有sizeof.goto.const这些关键字,但不能用goto.const作为变量名,虽然可以用sizeof,但为啥非得要用这个呢. 2.十六进制数以0x或0X开头: ...
- JAVA String介绍、常量池及String、StringBuilder和StringBuffer得区别. 以及8种基本类型的包装类和常量池得简单介绍
一.概述 String是代表字符串的类,本身是一个最终类,使用final修饰,不能被继承. 二.定义方式 方式一:直接赋值法 String str1 = "hello"; 方式 ...