由于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数据类型、if判断语句

    python的数据类型: int(整型) float(浮点型) #相较c++,去除了char.long.longlong... str(字符串)    #同等c++ sting类型 list(列表) ...

  2. PHP7.2中安装mcrypt扩展的问题

    当前使用的是宝塔工具,PHP版本是7.2,据说只有7.1以下的版本才会默认安装了mcrypt功能. 所以的7.2的安装如下: yum install libmcrypt libmcrypt-devel ...

  3. windows开发各种dll缺失

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

  4. Spring MVC 集成Disconf

    1.Disconf:Distributed Configuration Management Platform(分布式配置管理平台),专注于各种「分布式系统配置管理」的「通用组件」和「通用平台」, 提 ...

  5. mysql添加外键无法成功的原因

    最近很忙,碰到很多问题都忘了发上来做个记录,现在又忘了,FUCK,现在碰到一个问题, 就是mysql添加外键总是无法成功,我什么都试了,就是没注意signed和unsigned,FUCK,因为我用my ...

  6. Django08-批量创建数据

    通过views.py文件中创建 第1种方法循环创建数据, 这种方法不推荐,因为每一次循环都会连接一次数据库,效率较慢 def user_list(request): user_all = models ...

  7. 常用的js代码合集

    !function(util){ window.Utils = util(); }( function(){ //document_event_attributes var DEA = "d ...

  8. php+mysql 原生事务回滚

    <?php $conn = mysql_connect('127.0.0.1', 'root', ''); mysql_select_db('msc_test'); mysql_query('S ...

  9. Echart ,X轴显示的为tooltip内显示的一部分内容放在上面显示的一部分如下图所示

    如图所示:X轴只显示tooltip部分内容解决方案 在xAxis下面,实现方法如下 axisLabel: { interval: 0, formatter:function(value) { var ...

  10. 搭建redis-sentinel(哨兵机制)集群

    redis怎么才能做到高可用 对于redis主从架构,slave可以对应多个本身可以保障高可用,但是对于一个master节点,如果宕机,整个缓存系统就无法进行写的操作,显然整个系统会无法做到高可用 s ...