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 存放值的结构体,其值有三种,分别为 ...
随机推荐
- 关于一键提取QQ群成员信息的记录
问题情境 昨天晚上回宿舍看到舍友在吐槽:天哪,我要把70多个人的QQ号全统计出来,只能一个一个地在咱们学院的学院群里找.吐了. 当时我第一反应是用python写个脚本之类的去做,因此查阅了一下资料,找 ...
- C++ 子函数参数传递过程
编译环境:Visual Studio 2015 参数传递与汇编语言有很大关系.子函数传递参数主要方式有三种(这三种参数传递方式都可用用于x86汇编语言甚至其它汇编语言): 寄存器方式传递参数 存储器方 ...
- 计算机网络再次整理————tcp例子[五]
前言 本文介绍一些tcp的例子,然后不断完善一下. 正文 服务端: // See https://aka.ms/new-console-template for more information us ...
- ApacheCN Vue 译文集 20211115 更新
使用 GraphQL 构建 VueJS 应用 零.前言 一.数据绑定.事件和计算属性 二.组件.混合器和功能组件 三.设置我们的聊天应用--AWS Amplify 环境和 GraphQL 四.创建自定 ...
- Web:移动端阻止默认行为的小坑
问题 移动端 web 开发中,使用 addEventListener 阻止了 touchstart 事件的默认行为却发现没有生效 描述 再移动端 web 开发中,我们一般会用 addEventList ...
- 【XR-4】文本编辑器
直接做是困难的,不妨依照部分分来思考. - Subtask 3 首先会进入一个误区:维护修改,通过循环串的性质在 \(\tt KMP\) 自动机上优化遍历. 但可以发现这样很难处理,我们不妨 直接维护 ...
- resp.getWriter().print(categoryList)、resp.getWriter().write(String)与new ObjectMapper().writeValue(resp.getOutputStream(),categoryList)的区别
前言:最近在复习原生的servlet的时候,对其输出流不理解,故总结一下: resp.getWriter().print(categoryList) 可以输出字符串,也可以输出对象,可能还有其他类型, ...
- 【转】性能测试报告模板 V1.0
1. 测试项目概述与测试目的 1.1 项目概述 本部分主要是针对即将进行压力测试的对象(接口.模块.进程或系统)进行概要的说明,让人明白该测试对象的主要功能与作用及相关背景. 1.2 测试目标 简 ...
- The official raywenderlich.com Objective-C style guide.
The official raywenderlich.com Objective-C style guide. This style guide outlines the coding convent ...
- Python—高级函数
Python-高级函数 一.闭包 Python函数是支持嵌套的.如果在一个内部函数中对外部函数作用域(非全局作用域)的变量进行引用,那么内部函数就会被称为闭包.闭包需要满足如下3个条件: 存在于两个嵌 ...