由于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的更多相关文章

  1. 深入理解golang:sync.map

    疑惑开篇 有了map为什么还要搞个sync.map 呢?它们之间有什么区别? 答:重要的一点是,map并发不是安全的. 在Go 1.6之前, 内置的map类型是部分goroutine安全的,并发的读没 ...

  2. golang 标准库 sync.Map 中 nil 和 expunge 区别

    本文不是 sync.Map 源码详细解读,而是聚焦 entry 的不同状态,特别是 nil 状态和 expunge 状态的区分. entry 是 sync.Map 存放值的结构体,其值有三种,分别为 ...

  3. 源码解读 Golang 的 sync.Map 实现原理

    简介 Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent ...

  4. 总结golang之map

    总结golang之map 2017年04月13日 23:35:53 趁年轻造起来 阅读数:18637 标签: golangmapgo 更多 个人分类: golang   版权声明:本文为博主原创文章, ...

  5. sync.Map与Concurrent Map

    1. sync.Map 1.1. map并发不安全 go1.6以后map有了并发的安全检查,所以如果在并发环境中读写map就会报错 func unsafeMap() { // 创建一个map对象 m ...

  6. 深度解密 Go 语言之 sync.map

    工作中,经常会碰到并发读写 map 而造成 panic 的情况,为什么在并发读写的时候,会 panic 呢?因为在并发读写的情况下,map 里的数据会被写乱,之后就是 Garbage in, garb ...

  7. 看过这篇剖析,你还不懂 Go sync.Map 吗?

    hi, 大家好,我是 haohongfan. 本篇文章会从使用方式和原码角度剖析 sync.Map.不过不管是日常开发还是开源项目中,好像 sync.Map 并没有得到很好的利用,大家还是习惯使用 M ...

  8. Go 1.9 sync.Map揭秘

    Go 1.9 sync.Map揭秘 目录 [−] 有并发问题的map Go 1.9之前的解决方案 sync.Map Load Store Delete Range sync.Map的性能 其它 在Go ...

  9. Golang中map的三种声明方式和简单实现增删改查

    package main import ( "fmt" ) func main() { test3 := map[string]string{ "one": & ...

随机推荐

  1. Python爬虫简单介绍

    相关环境: Python3 requests库 BeautifulSoup库 一.requests库简单使用 简单获取一个网页的源代码: import requests sessions = requ ...

  2. (python基础 函数)

    关键字参数:关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值.使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值 必需参数:必 ...

  3. windows开发各种dll缺失

    推荐使用 depend这个工具,非常好用 http://www.dependencywalker.com/ 打开对应的文件,就能看到缺失了哪些dll 感谢博主: https://blog.csdn.n ...

  4. 服务器上运行程序Out of memory 解决办法

    ****** 服务器上跑过程序经常能遇到out of memory 这个问题,下面是我经常在实验室碰到的解决方法. 1.使用命令nvidia-smi,看到GPU显存被占满: 2.尝试使用 ps aux ...

  5. JAVA的第二次作业

    1.编写“人”类及其测试类.1.1 “人”类: 类名:Person 属性:姓名.性别.年龄.身份证号码 方法:在控制台输出各个信息1.2 测试类 类名:TestPerson 方法:main ...

  6. if __name__ == '__main__':用法

    这个博主写的很好,已经验证过了.https://blog.csdn.net/yjk13703623757/article/details/77918633

  7. 关于VMware虚拟机安装镜像时黑屏的解决办法

    新下载的VMware14,设置了新的虚拟机,镜像放的是ubuntu系统,然后开机要安装系统的时候黑屏,并没有进入到安装系统的界面 解决办法:在管理员权限下打开cmd,然后运行netsh winsock ...

  8. SQL Server 2016 在Windows Server 2012 R2 上的初步安装与远程连接实战(一):初步配置

    1.安装过程没有什么说头 2.下载并安装SSMS(SQLServer Management Studio),目前已更新到2017.1 3.使用SSMS登陆数据库 第一次选择Windows 身份验证,可 ...

  9. vue-cli使用vux时报错处理,“You may need an appropriate loader to handle this file type”

    先说解决方案: 在项目中找到build,找到webpack.base.conf.js 将vux给出的解决方案代码拷贝出来 const vuxLoader = require('vux-loader') ...

  10. Java读取excel数据保存入库

    Java开发读取excel表格数据入库保存: List<Map<String, Object>> list = null; String filePath = filePath ...