• Go语言sync.Map


    Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。
    如果想实现并发线程安全有两种方法:

    • map加互斥锁或读写锁
    • 标准库sync.map(Go1.19+新特性)

    sync.map源码

    https://github.com/golang/go/blob/master/src/sync/map.go

    sync.map 实现原理及优化

    • 利用map只读不用锁,通过冗余 read 和 dirty 两个字段将读写分离,读的数据存在只读字段 read 上,将最新写入的数据则存在 dirty 字段上,只在dirty读写上加锁,提高程序只读效率。
    • 读取时会先查询 read,不存在再查询 dirty,写入时则只写入 dirty
    • 读取 read 并不需要加锁,而读或写 dirty 都需要加锁
    • 另外有 misses 字段来统计 read 被穿透的次数(被穿透只需要读 dirty 的情况),超过一定次数则将 dirty 数据同步到 read 上
    • 对于删除数据则直接通过标记来延迟删除

    具体数据结构可参考:
    https://blog.csdn.net/u010853261/article/details/103848666
    https://www.haohongfan.com/docs/gohandbook/sync-chapter/2021-05-10-sync-map/

    sync.map 使用场景

    map+Mutex: 通过Mutex互斥锁来实现多个goroutine对map的串行化访问,读写都需要通过Mutex加锁和释放锁,适用于读写比接近的场景
    map+RWMutex:通过RWMutex来实现对map的读写进行读写锁分离加锁,从而实现读的并发性能提高,同Mutex相比适用于读多写少的场景
    sync.Map:底层通分离读写map和原子指令来实现读的近似无锁,并通过延迟更新的方式来保证读的无锁化。读多修改少,元素增加删除频率不高的情况,在大多数情况下替代上述两种实现

    sync.map 使用方法如下

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    func main() {
    	var m sync.Map
    	//添加一个元素
    	m.Store(1, "a")
    	m.Store("a", 2)
    
    	//读取一个元素
    	fmt.Println(m.Load(1))   //a true
    	fmt.Println(m.Load("a")) //2 true
    
    	//读取不存在的元素
    	fmt.Println(m.Load(2)) // false
    
    	//存在就返回,不存在就插入
    	fmt.Println(m.LoadOrStore("3", 33)) //33 false
    	fmt.Println(m.Load("3"))            //33 true
    
    	//如果存在的话,同时删除这个 key
    	fmt.Println(m.LoadAndDelete("3")) // 33 true
    
    	//删除某个元素
    	m.Delete("3")
    	// 遍历所有sync.Map中的键值对
    	m.Range(func(k, v interface{}) bool {
    		fmt.Println("iterate:", k, v)
    		return true
    	})
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    通过以上示例可以看到sync.map具有以下特性:

    • 可以存储不同的数据类型在一起,这有别于map只能存储声明好的数据类型,且相同的。
    • 无须初始化,直接声明即可。
    • sync.Map 不能使用map的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除
    • 增加了两个特殊方法LoadOrStore 存在就返回,不存在就插入,LoadAndDelete 如果存在的话,同时删除这个 key
    • 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。

    参考资料

    https://juejin.cn/post/6844904100287496206
    https://pkg.go.dev/sync#Map
    https://blog.csdn.net/u010853261/article/details/103848666
    https://medium.com/@deckarep/the-new-kid-in-town-gos-sync-map-de24a6bf7c2c
    https://www.haohongfan.com/docs/gohandbook/sync-chapter/2021-05-10-sync-map/

  • 相关阅读:
    Pytorch实战 | 第4天:猴痘病识别
    计算机毕业设计(附源码)python缘来有交友平台系统
    【语义分割】数据增强方法(原图与标签同时扩增)
    .NET 云原生架构师训练营(权限系统 代码实现 ActionAccess)--学习笔记
    Dockerfile定制Ubuntu的docker镜像
    MonkeyRunner自动化测试
    【无标题】
    HPE财报:计算存储微降,智能边缘大幅增长
    springBoot集成websocket实现消息实时推送提醒
    新媒体时代如何做好新型的网络口碑营销?
  • 原文地址:https://blog.csdn.net/guyan0319/article/details/127560848