由于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. php curl请求页面数据

    /** * * [curl_post post方式请求] * * @param [type] $url [description] * * @param string $data [descripti ...

  2. sql获取时间段内的所有日期

    ,'2015-01-01 00:00:00') h INTO vinson_h DELETE vinson_h DECLARE @h int SELECT @h=DATEDIFF(HOUR,'2015 ...

  3. QTP测试.NET程序的时候,ComboBox下拉框控件选择后,运行时对象不可见解决方案

    解决方法: 录制时,选择下拉框数据的时候,不要鼠标单击选择,而是要用ENTER(回车键)来选择,才能完成选择,这样录制就OK了.

  4. TypeError: Fetch argument 0 has invalid type <type 'int'>, must be a string or Tensor. (Can not convert a int into a Tensor or Operation.)

    6月5日的時候,修改dilated_seg.py(使用tensorflow)出現了報錯: TypeError: Fetch argument 0 has invalid type <type ' ...

  5. pyqt-QGrapicsView 坐标系详解

    PTQT——GraphicsView框架 转载 原网址 http://blog.51cto.com/9291927/1879128 一.GraphicsView框架简介 QT4.2开始引入了Graph ...

  6. swift-UIPickerView(选择控件)

    import UIKit //UIPickerView 的委托协议是 UIPickerViewDelegate,数据源是 UIPickerViewDataSource.我们需要在视图控制器中声明实现 ...

  7. nohup 写法

    nohup 空格 php程序路径 空格 php脚本路径 >> .out日志路径 2>&1 &

  8. 【18/12/31】hashcat源码粗读 --- sha256部分

    还没有详细研究过sha256算法的详细原理,主要是移植cf10算法时,hashcat在cf10_parse_hash时并不是直接调用sha256_update和sha256_final, 而是为了pr ...

  9. 转载——JavaScript学习笔记:取数组中最大值和最小值

    转载自:http://www.w3cplus.com/javascript/calculate-the-max-min-value-from-an-array.html. 取数组中最大值 可以先把思路 ...

  10. WEBLOGIC because another WebLogic Server instance is already using this directory

    错误提示:because another WebLogic Server instance is already using this directory 原因:ldap目录数据库文件被锁定,可能是w ...