当前位置:网站首页>Golang map 并发读写问题源码分析
Golang map 并发读写问题源码分析
2022-06-27 17:58:00 【Lynalmost】
map介绍及问题描述
map主要用来存储kv数据,其底层使用的是开链法去冲突的hashtable,拥有自动扩容机制。使用map最方便的一点是可以O(1)快速查询(目前slice并没有提供查询接口,只能通过自己写算法实现某个元素是否存在)。
map虽然好用,但是可能不适用。
但是map有一个非常致命的坑点,在并发场景下,并发读/写都可能会出现fatal error:concurrent map read and map write的错误,刚开始使用map的时候天真的认为只要不对同一个key进行并发操作就行,但是现实很骨感。测试时并发量很小的时候可能不会存在问题(只是运气好),并发量一大就会有问题。
但是不是所有场景下并发使用map都是不安全的
这是golang的官方文档,上面提到了只要有更新的操作存在,map就是非线程安全的,但是如果使用场景只是并发读,不涉及到写/删操作,那么就是并发安全的。
源码分析
定义
map head中flags字段,记录了当前map的一些状态,其中hashWriting就是造成并发读写map报错的“罪魁祸首”。
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
} // flags
iterator = 1 // there may be an iterator using buckets
oldIterator = 2 // there may be an iterator using oldbuckets
hashWriting = 4 // a goroutine is writing to the map
sameSizeGrow = 8 // the current map growth is to a new map of the same size写入
- 向map中新增元素最终会调用
mapassign函数,在新增操作开始之前就会检验flags的hashWriting位是否为1,为1则会报错。 - 检验通过后会将该位置为1,标记当前正在写入。
- 写入完成后将该位置为0
// Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
...
if h.flags&hashWriting != 0 {
throw("concurrent map writes")
}
hash := t.hasher(key, uintptr(h.hash0))
// Set hashWriting after calling t.hasher, since t.hasher may panic,
// in which case we have not actually done a write.
h.flags ^= hashWriting
...
done:
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
h.flags &^= hashWriting
...读取
读取数据的过程相对简单,在读取之前判断是否有置位,校验通过则可以进行读操作,读操作时不会进行置位的。
这也是为啥,如果一个map被初始化ok之后,只要不做增删改,并发读报错的。
// mapaccess1 returns a pointer to h[key]. Never returns nil, instead
// it will return a reference to the zero object for the elem type if
// the key is not in the map.
// NOTE: The returned pointer may keep the whole map live, so don't
// hold onto it for very long.
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
...
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
}
...
}结论
1.看过源码之后,发现这很像一个读写锁,但是并不会造成任何阻塞,有问题直接throw。
2.如果真的有初始化一次之后,一直并发读的场景,可以大胆使用map。
常见解决方案
1.自己加锁读。
2.使用sync.map替代(看过一点原理,写数据时实现了加锁;使用了空间换时间的方式,用两个哈希结构存储Map,有一层缓存,加速读取数据。)
3.使用二维切片替代,将key和index做映射。
#如果有理解不到位或者理解失误的地方~欢迎指正~~
边栏推荐
- 判断一个变量是数组还是对象?
- Code and principle of RANSAC
- Kotlin微信支付回调后界面卡死并抛出UIPageFragmentActivity WindowLeaked
- C# 二维码生成、识别,去除白边、任意颜色
- 海底电缆探测技术总结
- 谈谈线程安全
- 循环遍历及函数基础知识
- Tupu digital twin intelligent energy integrated management and control platform
- Cdga | what is the core of digital transformation in the transportation industry?
- 多伦多大学博士论文 | 深度学习中的训练效率和鲁棒性
猜你喜欢

GIS遥感R语言学习看这里

GIS remote sensing R language learning see here

What is ssr/ssg/isr? How do I host them on AWS?

DCC888 :Register Allocation

308. 二维区域和检索 - 可变 线段树/哈希

海底电缆探测技术总结

今晚战码先锋润和赛道第2期直播丨如何参与OpenHarmony代码贡献

一种朴素的消失点计算方法

Solution of adding st-link to Huada MCU Keil

Jinyuan's high-end IPO was terminated: it was planned to raise 750million Rushan assets and Liyang industrial investment were shareholders
随机推荐
1027 Colors in Mars
一种朴素的消失点计算方法
Solution of adding st-link to Huada MCU Keil
ABAP-SM30删除前检查
PCB线路板蛇形布线要注意哪些问题?
Leetcode 989. 数组形式的整数加法(简单)
Blink SQL内置函数大全
NVIDIA Clara-AGX-Developer-Kit installation
Redis 原理 - String
RANSAC的代码和原理
今晚战码先锋润和赛道第2期直播丨如何参与OpenHarmony代码贡献
[cloud based co creation] the "solution" of Digital Travel construction in Colleges and Universities
GIS remote sensing R language learning see here
PyCharm常用功能 - 断点调试
使用MySqlBulkLoader批量插入数据
图扑数字孪生智慧能源一体化管控平台
金鱼哥RHCA回忆录:DO447管理项目和开展作业--创建作业模板并启动作业
binder hwbinder vndbinder
Buzzer experiment based on stm32f103zet6 library function
On thread safety