Go 语言为什么不支持并发读写 map?
大家好,我是 frank ,「 Golang 语言开发栈」公众号作者。
01 介绍
在 Go 语言项目开发中,我们经常会使用哈希表 map,它的时间复杂度是 O(1),Go 语言中的 map 使用开放寻址法避免哈希碰撞。
Go 语言中的 map 并非原子操作,不支持并发读写操作。
Go 官方认为 map 在大多数情况下是使用 map 进行并发读操作,仅在少数情况下是使用 map 进行并发读写操作。
如果 Go 语言中的 map 原生支持并发读写操作,在操作时需要先获取互斥锁,反而会降低只有并发读操作时的性能。
在需要并发读写操作 map 时,可以结合 sync 包中的互斥锁一起使用。
02 并发读写 map
Go 支持并发读 map,不支持并发读写 map。
示例代码:
func main() {
var m = make(map[int]string)
go func() {
for {
m[1] = "xx"
}
}()
go func() {
for {
_ = m[1]
}
}()
time.Sleep(time.Second * 3)
}
输出结果:
fatal error: concurrent map read and map write
// ...
阅读上面这段代码,我们并发读写 map 类型的变量 m,在运行时,返回致命错误 fatal error: concurrent map read and map write。
Go 语言中的 map 在运行时是怎么检测到 map 的存在写操作?
源码:
const (
// 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
)
// A header for a Go map.
type hmap struct {
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
}
// 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 {
// ...
done:
if h.flags&hashWriting == 0 {
fatal("concurrent map writes")
}
h.flags &^= hashWriting
if t.IndirectElem() {
elem = *((*unsafe.Pointer)(elem))
}
return elem
}
阅读上面这段源码,我们可以发现在 hmap 结构体中的字段 flags,该字段用于标记 map 是否为写入状态。
在访问 map 时,通过判断 hmap.flags 和 hashWriting 的值,可知是否有其它 goroutine 访问 map,如果有,则返回致命错误 fatal("concurrent map writes")。
03 总结
本文介绍 Go 语言为什么不支持并发读写 map,Go 官方的说法是在多数情况下 map 只存在并发读操作,如果原生支持并发读写,即降低了并发读操作的性能。
通过阅读源码,我们了解到在运行时检测是否存在对 map 的写操作,如果存在,则返回致命错误。
读者朋友们在使用 map 时,要特别注意是否存在对 map 的并发写操作,如果存在,要结合 sync 包的互斥锁一起使用。
Go 语言为什么不支持并发读写 map?的更多相关文章
- c++ 11开始语言本身和标准库支持并发编程
c++ 11开始语言本身和标准库支持并发编程,意味着真正要到编译器从语言和标准库层面开始稳定,估计得到17标准出来.14稳定之后的事情了,根据历史经验,新特性的引入到稳定被广泛采用至少要一个大版本的跨 ...
- golang中map并发读写问题及解决方法
一.map并发读写问题 如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误 如下代码很容易就出现map并发读写问题 ...
- go语言坑之并发访问map
fatal error: concurrent map read and map write 并发访问map是不安全的,会出现未定义行为,导致程序退出.所以如果希望在多协程中并发访问map,必须提供某 ...
- python语言(五)匿名函数、读写excel、操作数据库、加密、redis操作
一.匿名函数 递归:就是调用自己 def func(): num = int(input('num:')) if num % 2 ==0: print('是偶数') return else: func ...
- php中并发读写文件冲突的解决方案(文件锁应用示例)
PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适 ...
- java并发容器(Map、List、BlockingQueue)
转发: 大海巨浪 Java库本身就有多种线程安全的容器和同步工具,其中同步容器包括两部分:一个是Vector和Hashtable.另外还有JDK1.2中加入的同步包装类,这些类都是由Collectio ...
- R语言使用RMySQL连接及读写Mysql数据库 测试通过
R语言使用RMySQL连接及读写Mysql数据库 简单说下安装过程,一般不会有问题,重点是RMySQL的使用方式. 系统环境说明 Redhat系统:Linux 460-42.6.32-431.29.2 ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- java并发容器(Map、List、BlockingQueue)具体解释
Java库本身就有多种线程安全的容器和同步工具,当中同步容器包含两部分:一个是Vector和Hashtable.另外还有JDK1.2中增加的同步包装类.这些类都是由Collections.synchr ...
- gorm操作sqlite3,高并发读写如何避免锁库?
1. 场景 这两天一直被这个sqlit3困扰,起因是项目中需要有这样一个中间,中间件承担着API角色和流量转发的角色,需要接收来自至少300个agent的请求数据,和健康检测的请求. 所以当即想到用g ...
随机推荐
- 小提琴图的绘制方法:Python matplotlib实现
本文介绍基于Python中matplotlib模块与seaborn模块,利用多个列表中的数据,绘制小提琴图(Violin Plot)的方法. 小提琴图作为一种将箱型图与核密度图分别所能表达的信 ...
- addEventListener学习
场景:给input框添加事件,但是里面的function得抽取出来复用,并且这个function还要传递参数 userId.addEventListener('input', idTest(userI ...
- centos7安装node-v18版本真是难呢
背景 背景就是上一篇文章提到的,部署gitbook这个文档中心的话,是需要先安装node,然后,如果你的node版本过高的话,一般会报错,此时,网上很多文章就是降node版本解决,但其实用高版本也是有 ...
- 装箱问题(lgP1049)
01背包问题. 与模板不同,这道题要求的是最小剩余空间,也就是求背包里最多能放多少东西. 所以状态转移方程变为 fi= fi - w[i] + wi . 其中 fi 表示当背包容量为 i 时可放的最大 ...
- Java线程安全详解
并发与多线程 blog:https://devonmusa.github.io 1 常见概念 1.1 操作系统线程运行状态 NEW RUNNABLE RUNNING BLOCKED 1.2 Java虚 ...
- 一元多项式求和(c++源码)
LinkList.h #ifndef LINKLIST_H_ #define LINKLIST_H_ #include<stdio.h> template<class T> s ...
- Flutter搭建
目录 下载 Flutter SDK 配置 Flutter 环境变量及镜像 检查开发环境 参考 下载 Flutter SDK flutter官网下载:https://flutter.io/sdk-arc ...
- 聊聊分布式 SQL 数据库Doris(七)
LSM-Tree Doris的存储结构是类似LSM-Tree设计的,因此很多方面都是通用的,先阅读了解LSM相关的知识,再看Doris的底层存储与读取流程会清晰透彻很多,如下是几个关键的设计: SST ...
- Vite4+Typescript+Vue3+Pinia 从零搭建(6) - 状态管理pina
项目代码同步至码云 weiz-vue3-template pina 是 vue3 官方推荐的状态管理库,由 Vue 核心团队维护,旨在替代 vuex.pina 的更多介绍,可从 pina官网 查看 特 ...
- javascript+php 实现blob加密视频(html video)
1.mp4地址加密为blob链接在html5的video标签展示 PHP: 1 $file_path = "...mp4"; //视频文件地址 2 ob_end_clean(); ...