Go 并发编程实战课
晁岳攀(鸟窝)
前微博技术专家,知名微服务框架 rpcx 作者
25635 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 22 讲
Go 并发编程实战课
15
15
1.0x
00:00/00:00
登录|注册

09 | map:如何实现线程安全的map类型?

延迟删除
double-checking
动态调整
优先从read字段读取、更新、删除
空间换时间
适用于特定场景,性能更好
减少锁的粒度和持有时间
扩展map,支持并发读写
容易导致panic异常
map对象必须在使用之前初始化
一个值或两个值
可比较的数据类型
sync.Map元素删除的时机
sync.Map中的双检查机制
性能测试的重要性
各种线程安全的map实现
map类型的并发问题
sync.Map的实现
应对特殊场景的sync.Map
分片加锁
加读写锁
并发读写
未初始化
map[key]函数返回结果
key类型的注意点
思考题
总结
sync.Map
实现线程安全的map类型
常见错误
基本使用方法
线程安全的map类型

该思维导图由 AI 生成,仅供参考

你好,我是鸟窝。
哈希表(Hash Table)这个数据结构,我们已经非常熟悉了。它实现的就是 key-value 之间的映射关系,主要提供的方法包括 Add、Lookup、Delete 等。因为这种数据结构是一个基础的数据结构,每个 key 都会有一个唯一的索引值,通过索引可以很快地找到对应的值,所以使用哈希表进行数据的插入和读取都是很快的。Go 语言本身就内建了这样一个数据结构,也就是 map 数据类型
今天呢,我们就先来学习 Go 语言内建的这个 map 类型,了解它的基本使用方法和使用陷阱,然后再学习如何实现线程安全的 map 类型,最后我还会给你介绍 Go 标准库中线程安全的 sync.Map 类型。学完了这节课,你可以学会几种可以并发访问的 map 类型。

map 的基本使用方法

Go 内建的 map 类型如下:
map[K]V
其中,key 类型的 K 必须是可比较的(comparable),也就是可以通过 == 和 != 操作符进行比较;value 的值和类型无所谓,可以是任意的类型,或者为 nil。
在 Go 语言中,bool、整数、浮点数、复数、字符串、指针、Channel、接口都是可比较的,包含可比较元素的 struct 和数组,这俩也是可比较的,而 slice、map、函数值都是不可比较的。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言内建map类型存在并发访问问题,本文介绍了其基本使用方法和常见错误,以及解决方案。作者详细介绍了如何利用读写锁实现线程安全的map类型,避免并发读写panic,并介绍了分片加锁的方法以提高并发性能。此外,还介绍了Go官方线程安全map的标准实现sync.Map及其优化点。总的来说,本文对于学习如何实现线程安全的map类型的读者具有很高的参考价值。文章还提到了sync.Map适合增长缓存系统,不适合频繁删除和新增元素,以及一些思考题供读者深入探讨。文章内容丰富,涵盖了map类型的并发访问问题及解决方案,适合对Go语言并发编程感兴趣的读者阅读。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 并发编程实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(23)

  • 最新
  • 精选
  • 蜉蝣
    老师好,我看到 read 中 key 被删除会有两个状态:nil 和 expunged。我会有些不明白,要么都用 nil 或者都用 expunged,这样会不会更好一些?

    作者回复: 第一你说的没错:nil和expunged都代表元素被删除了,只不过expunged比较特殊,如果被删除的元素是expunged,代表它只存在于readonly之中,不存在于dirty中。这样如果重新设置这个key的话,需要往dirty增加key

    2020-11-12
    3
    8
  • tingting
    想问一下老师,以下这种情况会有data race吗? m:=make(map[string]int) Goroutine A: 不停地覆盖m指向新的map值 Goroutine B: 不停地读m里面的某个key

    作者回复: 会。 遇到这样的问题不如写个测试验证一下

    2022-03-24
  • 叶小彬
    老师,我看了sync.Map 源码,有两点不是很懂 1、设计read和dirty的想法是什么 因为sync.Map里,read结构本身就是atomic.Value,增加和修改有Store方法,本身就可以防止幻读,脏读的问题,如果是为了delete的逻辑(我发现atomic.Value里是没有delete方法的),那完全可以写一个加锁逻辑的delete,个人感觉dirty的没什么用 2、源码里的逻辑是,当read 的miss次数大于等于dirty的长度的时候,就将dirty转成read,这个是什么设计想法

    作者回复: 只读不需要加锁,快

    2021-11-07
  • 徐改
    还是不太明白为什么在创建dirty的时候,要将read中未删除的entry拷贝给dirty. sync.map一个优秀的地方是当我们访问read的时候不需要上锁,访问dirty的时候需要加锁。在Load()方法中,我们每次都是先访问read,如果read中没有的话才访问dirty。那么对于dirty来说,dirty中的数据可能read没有,或者read有。read中有的数据,dirty有;read中没有的数据,dirty可能会有。而我们的程序每次都是先访问read,如果read没有后续才会访问dirty,那这样的话创建dirty的时候,感觉可以不用将read中的entry一个一个拷贝到dirty中,因为我们访问是先访问read的。

    作者回复: 因为dirty将来可能转为readonly

    2021-10-27
  • 校歌
    老师,发现有个地方不严谨,”map不可比较”。我写了个小程序,提示map只可以跟nil比较,而不是不能比较。(可能有点扣字眼了) ./main.go:11:12: invalid operation: resMap == resMap (map can only be compared to nil)

    作者回复: 是的,准确的说可以和nil比较

    2021-05-14
  • 新味道
    新加的元素需要放入到 dirty 中,如果 dirty 为 nil,那么需要从 read 字段中复制出来一个 dirty 对象。 --- 为什么需要从 read 字段中复制出来一个 dirty 对象?

    作者回复: 因为dirty中也要能查到这些值的

    2020-11-26
  • 约书亚
    应该着重说明一下为什么有expunged这种状态,这点比较迷惑。我能理解expunged的entry代表read中存在而dirty中不存在。但为什么在read向dirty复制时,需要将nil的entry变为expunged?

    作者回复: nil和expunged都代表元素被删除了,只不过expunged比较特殊,如果被删除的元素是expunged,代表它只存在于readonly之中,不存在于dirty中。这样如果重新设置这个key的话,需要往dirty增加key

    2020-11-13
    3
  • Panmax
    文章中写到「所以,这里我们就超前一把,我带你直接体验这个即将要发布的泛型方案。」 是我对泛型的理解有什么误会吗,下文中并没有看到使用泛型的地方������。

    作者回复: 这句话应该删除,谢谢指出

    2020-10-31
  • Junes
    1. 双检查主要是针对高并发的场景: 第一次先用CAS快速尝试,失败后进行加锁,然后进行第二次CAS检查,再进行修改; 在高并发的情况下,存在多个goroutine在修改同一个Key,第一次CAS都失败了,在竞争锁;如果不进行第二次CAS检查就直接修改,这个Key就会被多次修改; 2. 真正删除key的操作是在数据从read往dirty迁移的过程中(往dirty写数据时,发现dirty没有数据,就会触发迁移),只迁移没有被标记为删除的KV
    2020-10-30
    5
    32
  • 我来也
    看到本文的标题,就让我想到之前看过的一篇文章: [踩了 Golang sync.Map 的一个坑](https://gocn.vip/topics/10860) 就是老师文章代码中的一行注释的由来: `这一行长坤在1.15中实现的时候忘记加上了,导致在特殊的场景下有些key总是没有被回收` 当时我是好好把系统的sync.Map代码看了一下. 虽然才短短384行代码,但还是花了不少功夫. 另外,推荐一个 欧长坤 未完工的开源电子书 [Go 语言原本](https://github.com/golang-design/under-the-hood).
    2020-10-30
    1
    14
收起评论
显示
设置
留言
23
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部