由于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. JAVA图片验证码(转自“云在青山”)

    首先创建一个生成图片的类,设置一些请求参数,生成随机的字符串,然后字符串传给生成验证码图片的类进行处理,完成后输出到页面 1.创建验证码生成类 package hh.com.util; import ...

  2. 微服务架构基础之Service Mesh

    ServiceMesh(服务网格) 概念在社区里头非常火,有人提出 2018 年是 ServiceMesh 年,还有人提出 ServiceMesh 是下一代的微服务架构基础. 那么到底什么是 Serv ...

  3. Python12(接口继承,子类调用父类,多态)

    接口继承: 基类不用实现内部逻辑,只是为了规范子类,可以用abc模块中以添加装饰器的方式实现 import abc class All_file(metaclass=abc.ABCMeta): @ab ...

  4. 海外aws-ubuntu-16.04系统使用ansible安装tidb

    本以为按照官方文档会比较顺利,没想到还是遇到了几个坑,毕竟pingcap不能考虑到所有问题 环境: 1.ubuntu:16.04LTS 2.python:2.7 小坑:因为第一次使用ubuntu,没想 ...

  5. [记录] Vue中的dom操作

    使用过Vue的同学都应该有这样一个感觉,在vue中页面是基于数据驱动的,不需要我们自己操作dom,框架帮我们完成了这一步,事实上Vue官方也建议我们这样做 在绝大多数情况下是不需要操作dom就可以完成 ...

  6. 吴裕雄 python 机器学习——ElasticNet回归

    import numpy as np import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot ...

  7. 利用PIL创建验证码

    1. 随机生成rgb 元组 def random_RGB(min, max): return tuple([random.randint(min, max) for i in range(3)])2. ...

  8. 50代码HTML5 Canvas 3D 编辑器优雅搞定

    1024程序员节刚过,手痒想实现一个html的3d编辑器,看了three.js 同时还看了网上流传已久的<<基于 HTML5 Canvas 的简易 2D 3D 编辑器>>,都觉 ...

  9. python day10 函数(第二篇)

    2019.4.10 S21 day10笔记总结 一.内容回顾 1.面试题相关: 1.py2和py3的区别 2.运算符的计算 :3 or 9 and 8 3.字符串的反转 4.is和==的区别 5.v1 ...

  10. 基于DPDK的高效包处理系统

    一.概念 Intel® DPDK全称Intel Data Plane Development Kit,是intel提供的数据平面开发工具集,为Intel architecture(IA)处理器架构下用 ...