Golang:sync.Map
由于map在gorountine 上不是安全的,所以在大量并发读写的时候,会出现错误。
在1.9版的时候golang推出了sync.Map.
sync.Map
通过阅读源码我们发现sync.Map是通过冗余的两个数据结构(read、dirty),实现性能的提升。
为了提升性能,load、delete、store等操作尽量使用只读的read;
为了提高read的key命中概率,只有当read中读取不到的累计miss次数大于等于dirty的长度时,将dirty数据提升为read;
对于数据的删除,采用延迟标记删除法,只有在提升dirty的时候才删除。
数据结构
type Map struct {
// 读写dirty时使用的锁
mu Mutex
read atomic.Value
dirty map[interface{}]*entry
// 从read中读取不到,从dirty读取到数据时,+1
misses int
}
type readOnly struct {
m map[interface{}]*entry
amended bool
}
type entry struct {
//指针类型
p unsafe.Pointer
}
Delete
func (m *Map) Delete(key interface{}) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
if !ok && read.amended { //double check
delete(m.dirty, key)
}
m.mu.Unlock()
}
if ok {
e.delete()
}
}
func (e *entry) delete() (hadValue bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expunged {
return false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) { //原子操作,加删除标记
return true
}
}
}
删除时,如果read中没有,就直接从dirty删除。如果read中有,就把read中标记为删除。
Load
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key] // read中读取不到,从dirty读,miss++
m.missLocked()
}
m.mu.Unlock()
}
if !ok {
return nil, false
}
return e.load()
}
Load返回存储在映射中的键值(read中读取不到,从dirty读),如果没有值,则返回nil。ok结果指示是否在映射中找到值。
missLocked和Store
func (m *Map) missLocked() {
m.misses++
if m.misses < len(m.dirty) {
return
}
m.read.Store(readOnly{m: m.dirty})
m.dirty = nil
m.misses =
}
func (m *Map) Store(key, value interface{}) {
//如果在read 读取到,就原子操作直接对值进行更新
read, _ := m.read.Load().(readOnly)
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
//如果未在read 中读取到值或读取到值进行更新时更新失败,则加锁进行后续处理
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
if e, ok := read.m[key]; ok {
// double check,如果读取到的值处于删除状态,将值写入dirty map中
if e.unexpungeLocked() {
m.dirty[key] = e
}
// 原子操作更新key对应的值
e.storeLocked(&value)
} else if e, ok := m.dirty[key]; ok {
//如果在dirty map中读取到值,则直接使用原子操作更新值
e.storeLocked(&value)
} else {
//如果dirty map中不含有值,则说明dirty map已经升级为read map,或者第一次进入
//需要初始化dirty map,并将read map的key添加到新创建的dirty map中.
if !read.amended {
m.dirtyLocked()
m.read.Store(readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
}
m.mu.Unlock()
}
一些观点,当有大量并发读写发生的时候,会有很多的miss导致不断的dirty升级。可能会影响效率。
尝试使用sync.Map
package main import (
"fmt"
"sync"
) func main() {
var m sync.Map
m.Store(, "a")
m.Store(, "b")
m.Store(, "c")
m.Store(, "d") m.Range(func(k, v interface{}) bool {
fmt.Println(k, v)
return true
})
//LoadOrStore
v, ok := m.LoadOrStore(, "e")
fmt.Println(ok, v) v, ok = m.LoadOrStore(, "bbb")
fmt.Println(ok, v) //Load
v, ok = m.Load()
if ok {
fmt.Println("it's an existing key,value is ", v)
} else {
fmt.Println("it's an unknown key")
} m.Range(func(k, v interface{}) bool {
fmt.Println(k, v)
return true
}) m.Delete()
fmt.Println(m.Load()) }
d
a
b
c
false e
true a
it's an existing key,value is a
b
c
d
e
a
<nil> false
运行结果是:
Golang:sync.Map的更多相关文章
- 深入理解golang:sync.map
疑惑开篇 有了map为什么还要搞个sync.map 呢?它们之间有什么区别? 答:重要的一点是,map并发不是安全的. 在Go 1.6之前, 内置的map类型是部分goroutine安全的,并发的读没 ...
- golang 标准库 sync.Map 中 nil 和 expunge 区别
本文不是 sync.Map 源码详细解读,而是聚焦 entry 的不同状态,特别是 nil 状态和 expunge 状态的区分. entry 是 sync.Map 存放值的结构体,其值有三种,分别为 ...
- 源码解读 Golang 的 sync.Map 实现原理
简介 Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent ...
- 总结golang之map
总结golang之map 2017年04月13日 23:35:53 趁年轻造起来 阅读数:18637 标签: golangmapgo 更多 个人分类: golang 版权声明:本文为博主原创文章, ...
- sync.Map与Concurrent Map
1. sync.Map 1.1. map并发不安全 go1.6以后map有了并发的安全检查,所以如果在并发环境中读写map就会报错 func unsafeMap() { // 创建一个map对象 m ...
- 深度解密 Go 语言之 sync.map
工作中,经常会碰到并发读写 map 而造成 panic 的情况,为什么在并发读写的时候,会 panic 呢?因为在并发读写的情况下,map 里的数据会被写乱,之后就是 Garbage in, garb ...
- 看过这篇剖析,你还不懂 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中map的三种声明方式和简单实现增删改查
package main import ( "fmt" ) func main() { test3 := map[string]string{ "one": & ...
随机推荐
- Android 开发 关于APP无法安装到Android模拟器上的问题
我们在创建一个app后,打算安装到Android模拟器上.可能会出现无法安装,或者提示已经安装无法卸载的问题.这个时候需要添加兼容CPU. 选择 build.gradle 文件打开添加如下代码: an ...
- 树——B-树
B树的定义: 1.若根结点不是终端结点,则至少有2棵子树 2.每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m 3.每一个叶子节点都包含k-1个元素,其中 m/2 ...
- JConsole监控Linux上的Tomcat
JConsole监控Linux上的Tomcat 从Java 5开始引入了 JConsole,来监控 Java 应用程序性能和跟踪 Java 中的代码.jconsole是JDK自带监控工具,只需要找到 ...
- 列表推导式和sum的用法
作者:杨航锋链接:https://www.zhihu.com/question/57470958/answer/153405326来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- 视频剪辑软件调研:Adobe Premiere、会声会影、抖音短视频
Adobe Premiere.会声会影.抖音短视频基本功能特点对比: 特点 Adobe Premiere 会声会影 抖音短视频 运行平台 Win7/Win8/Win10.macOS Win7/Win ...
- 深度学习原理与框架- tf.nn.atrous_conv2d(空洞卷积) 问题:空洞卷积增加了卷积核的维度,为什么不直接使用7*7呢
空洞卷积, 从图中可以看出,对于一个3*3的卷积,可以通过使用增加卷积的空洞的个数,来获得较大的感受眼, 从第一幅图中可以看出3*3的卷积,可以通过补零的方式,变成7*7的感受眼,这里补零的个数为1, ...
- Hash算法总结(转)
1. Hash是什么,它的作用先举个例子.我们每个活在世上的人,为了能够参与各种社会活动,都需要一个用于识别自己的标志.也许你觉得名字或是身份证就足以代表你这个人,但是这种代表性非常脆弱,因为重名的人 ...
- 安装和破解Navicat
这里的是安装目录,要记住 额外热任务在桌面创建一个快捷方式 点击完成之后先不要打开运行Navicat.exe这个主程序,把PatchNavicat.exe复制到你刚刚我们安装的路径,,不是你安装包的路 ...
- Alpha冲刺
第一天 日期:2018/6/16 1.今日完成任务情况以及遇到的问题 张天旭:根据系统的需求,完成数据库的设计 周甜甜:完成系统后台登录界面的设计及登录功能的实现 李蕾:完成系统后台首页的设计 张海鑫 ...
- el-input的color修改无效问题
相信很多前端初学者跟我一样也遇到过el-input的color修改无效问题 如下图:我想把el-input里面的文字改成蓝色,但是使用总是失败 修改方法:打开调试界面,找到el-input对应的sty ...