GoLand map中的并发问题——为什么会造成并发问题?该怎么解决?

GoLand map中的并发问题——为什么会造成并发问题?该怎么解决?

  • 问题提出
  • 原因解析
    • 具体原因
    • 竞态检测器
  • 如何解决并发问题呢?
    • 方法一 : 使用sync.Mutex
    • 方法二: 使用sync.Map
      • 我们首先了解一下sync.Map的常用方法:
        • Store(key, value interface{})
        • Load(key interface{}) (value interface{}, ok bool)
        • LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
        • Delete(key interface{})
        • Range(f func(key, value interface{}) bool)
      • 修改之前的代码
  • 总结
  • 注释 : 竞态检测器

问题提出

大家在使用map的时候,一定遇到过一个问题,由于map并不是线程安全,所以就会导致并发问题的出现。

下面先给大家演示一下这个问题:

func main() {m := make(map[string]int, 2)m["dd"] = 22go func() {for {m["ff"] = 1}}()go func() {for {_ = m["dd"]}}()time.Sleep(1 * time.Hour)
}

会出现以下报错:

fatal error: concurrent map read and map write

为什么会抛出这个错误呢?


原因解析

具体原因

这个错误其实是“故意”设计给map的,在map的底层代码里写好的,为了避免map出现并发问题,用来确保数据的正确性的。

 if h.flags&hashWriting != 0 {fatal("concurrent map read and map write")}

map这么设计有两点好处:

  1. 保证了map的运行性能,不使用锁机制降低了程序运行的开销
  2. 避免了map运行时造成不可预期的错误。比如map的渐进式扩容,在没有并发的情况下,开启扩容的前提一定是没有处于扩容状态,才能让每一步操作分担运行成本;如果并发操作,没有办法保证在下一次扩容之前完成了前一次的渐进扩容。

竞态检测器

不过大家可能会发现,map源码中是有一个竞态检测器的代码

	// 如果启用了竞态检测并且h不为nil,进行竞态检测。if raceenabled && h != nil {callerpc := getcallerpc()pc := abi.FuncPCABIInternal(mapaccess1)racereadpc(unsafe.Pointer(h), callerpc, pc)raceReadObjectPC(t.Key, key, callerpc, pc)}

这个玩意干什么的呢?为啥有了这个东西还是不能避免并发问题呢?

  1. 这个东西在平时的生产环境是默认关闭的,开启需要在执行前输入"-race",像下面这样(如果运行有问题,见文末注释
go run -race main.go
  1. 这个东西只能用来检测并发程序中的竞态条件,并不能规避并发问题!

例如上面举例的并发错误代码,用-race运行结果是这样的

D:\GoLand 2024.1.1\program\test
go run -race main.go
==================
WARNING: DATA RACE
Write at 0x00c000020060 by goroutine 6:runtime.mapassign_faststr()D:/GoLand 2024.1.1/Go/src/runtime/map_faststr.go:203 +0x0main.main.func1()D:/GoLand 2024.1.1/program/test/main.go:12 +0x44Previous read at 0x00c000020060 by goroutine 7:runtime.mapaccess1_faststr()D:/GoLand 2024.1.1/Go/src/runtime/map_faststr.go:13 +0x0main.main.func2()D:/GoLand 2024.1.1/program/test/main.go:17 +0x44Goroutine 6 (running) created at:main.main()D:/GoLand 2024.1.1/program/test/main.go:10 +0xc5Goroutine 7 (running) created at:main.main()D:/GoLand 2024.1.1/program/test/main.go:15 +0x130
==================
fatal error: concurrent map read and map writegoroutine 6 [running]:
main.main.func2()D:/GoLand 2024.1.1/program/test/main.go:17 +0x45
created by main.main in goroutine 1D:/GoLand 2024.1.1/program/test/main.go:15 +0x131goroutine 1 [sleep]:
time.Sleep(0x34630b8a000)D:/GoLand 2024.1.1/Go/src/runtime/time.go:195 +0x126
main.main()D:/GoLand 2024.1.1/program/test/main.go:20 +0x145goroutine 5 [runnable]:
main.main.func1()D:/GoLand 2024.1.1/program/test/main.go:12 +0x45
created by main.main in goroutine 1D:/GoLand 2024.1.1/program/test/main.go:10 +0xc6
exit status 2

WARNING: DATA RACE 就意味着发生了并发问题,还有并发问题的详细信息


如何解决并发问题呢?

有两种常用方法

方法一 : 使用sync.Mutex

我们可以使用互斥锁(sync.Mutex)来保护map的并发访问。在写入或读取map之前,我们需要获取锁,以确保同一时间只有一个goroutine可以访问map。

type SafeMap struct {mu sync.Mutexm  map[string]int
}func NewSafeMap() *SafeMap {return &SafeMap{m: make(map[string]int),}
}func (sm *SafeMap) Set(key string, value int) {sm.mu.Lock()defer sm.mu.Unlock()sm.m[key] = value
}func (sm *SafeMap) Get(key string) (int, bool) {sm.mu.Lock()defer sm.mu.Unlock()val, ok := sm.m[key]return val, ok
}func main() {m := NewSafeMap()m.Set("dd", 22)go func() {for {m.Set("ff", 1)}}()go func() {for {_, _ = m.Get("dd")}}()time.Sleep(1 * time.Hour)
}

方法二: 使用sync.Map

我们首先了解一下sync.Map的常用方法:

  • Store(key, value interface{})

用于添加或更新键值对。如果键已存在,它的值将被新值覆盖。

var m sync.Map
m.Store("exampleKey", "exampleValue")
  • Load(key interface{}) (value interface{}, ok bool)

用于获取键对应的值。如果键存在,返回键对应的值和true;如果不存在,返回nil和false。

if value, ok := m.Load("exampleKey"); ok {fmt.Println("Value found:", value)
}
  • LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)

尝试从映射中加载键的值。如果键不存在,它将存储键值对到映射中。返回加载到的值(或存储的值)和一个布尔值,表示值是否被加载。

if actual, loaded := m.LoadOrStore("exampleKey", "newValue"); loaded {fmt.Println("Value loaded:", actual)
} else {fmt.Println("Value stored:", actual)
}
  • Delete(key interface{})

用于删除映射中的键及其对应的值。

m.Delete("exampleKey")
  • Range(f func(key, value interface{}) bool)

用于迭代映射中的所有键值对。它接受一个函数作为参数,该函数会被调用每个键值对。如果该函数返回false,迭代将停止。

m.Range(func(key, value interface{}) bool {fmt.Println("Key:", key, "Value:", value)return true // 继续迭代
})

修改之前的代码

func main() {var m sync.Mapm.Store("dd", 22)go func() {for {m.Store("ff", 1)}}()go func() {for {_, _ = m.Load("dd")}}()time.Sleep(1 * time.Hour)
}

总结

  • 为了保证性能,将map设置成了不可以并发
  • 想要并发操作map,可以使用sync.Mutex 或者sync.Map

注释 : 竞态检测器

  1. 大家在使用"-race"启动的时候,可能会遇到下面的问题:
go: -race requires cgo; enable cgo by setting CGO_ENABLED=1翻译:Go: -race要求Go;通过设置CGO_ENABLED=1开启cgo

解决方法 —— 使用env -w 修改环境变量的值:

go env -w CGO_ENABLED=1
  1. 之后可能还会出现下面的错误
cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in %PATH%

先把gcc安装一下,配置一下环境变量就可以了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024.5.24.python.exercise

# python文件操作 # f open("打字版.txt", "a", encoding"UTF-8") # writer input("请输入你想要写入到文件的内容") # f.write(writer) # f.flush() # f.close() # f open("打字版.txt", "r", encoding"…

代码随想录算法训练营第三十九天 | 738.单调递增的数字、968.监控二叉树 (可以跳过)

监控二叉树同样的等代码随想录刷完后,再回头来看,先跳过 738.单调递增的数字 代码随想录 解题思路 例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]--…

游戏引擎支持脚本编程的好处

哈喽呀,大家好,淼淼又来和大家见面啦,咱们今天来聊聊游戏引擎,游戏引擎作为现代游戏开发的核心,它集成了图形渲染、物理模拟、音频处理、动画系统、输入输出控制等多种复杂技术于一体,为开发者提供了一个高…

ASP+ACCESS基于WEB网上留言板

摘要 本文概述了ACCESS数据库及其相关的一些知识,着重论述ACCESS数据库和ASP的中间技术,构建一个简单的留言板。具体的实现是构造一个留言板系统,能很方便的和同学沟通和交流。留言板具有功能强大、使用方便的特点。用户以个人的身份进入&am…

瑞芯微RV1126——人脸识别源码分析

本节内容主要分为3部分,第一部分是流程结构图;第二部分为人脸识别代码流程;第三部分为具体的代码分析。 1.流程结构图 2.人脸识别代码流程 1、人脸数据的初始化: init_all_rockx_face_data();init_face_data();2、创建rtsp会话,这里包括发…

一个典型的分布式缓存系统是什么样的?no.32

分布式 Redis 服务 由于本课程聚焦于缓存,接下来,我将以微博内的 分布式 Redis 服务系统为例,介绍一个典型的分布式缓存系统的组成。 微博的 Redis 服务内部也称为 RedisService。RedisService 的整体架构如图所示。主要分为Proxy、存储、集…

产品推荐 | 基于Xilinx XCKU115的半高PCIe x8 硬件加速卡

一、板卡概述 本板卡系我公司自主研发,采用Xilinx公司的XCKU115-3-FLVF1924-E芯片作为主处理器,主要用于FPGA硬件加速。板卡设计满足工业级要求。如下图所示: 二、功能和技术指标 板卡功能 参数内容 主处理器 XCKU115-3-FLVF1924-E 板卡…

UE4/UE5像素流送云推流:多人访问不稳定、画面糊、端口占用多等

UE4/UE5想要实现网页访问,很多工程师会选择guan方的像素流送。但这个技术要求在模型开发初期就接入。对于一些已有UE模型是无法进行流化的。虽然也可以解决新UE模型的网页访问问题,但在实际的应用中,点量云流也收到很多反馈说,使用…

netty-socketio 集群随记

实现netty-socketio集群的方式 代码实例 PostConstructpublic void subscribe() {pubSubStore.subscribe(PubSubType.DISPATCH, new PubSubListener<DispatchMessage>() {Overridepublic void onMessage(DispatchMessage message) {log.debug("subscribe: {}"…

Python爬取B站视频:封装一下

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

大数据Hadoop之-工具HIVE(一)

大数据Hadoop之——数据仓库Hive HIVE介绍Hive是基于Hadoop的一个数据仓库(Data Aarehouse,简称数仓、DW),可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。是用于存储、分析、报告的数据系统。 在Hadoop生态系统中,HDFS用于存储数据,Yarn用于资源管理…

解释Spring Bean的生命周期

Spring Bean的生命周期涉及到Bean的创建、配置、使用和销毁的各个阶段。理解这个生命周期对于编写高效的Spring应用和充分利用框架的功能非常重要。下面是Spring Bean生命周期的主要步骤&#xff1a; 1. 实例化Bean Spring容器首先将使用Bean的定义&#xff08;无论是XML、注…

使用Golang调用腾讯云邮件模版发送邮件

文章目录 一、腾讯云邮件模版创建1.1 发信域名配置1.2 发信地址设置1.3 发信模版设置 二、通过Golang发送邮件2.1 代码示例2.2 代码说明 三、常见问题3.1 UnsupportedRegion3.2 InvalidTemplateID 本文档介绍了如何使用Golang编写代码&#xff0c;通过腾讯云邮件服务&#xff0…

【Linux】中的常见的重要指令(中)

目录 一、man指令 二、cp指令 三、cat指令 四、mv指令 五、more指令 六、less指令 七、head指令 八、tail指令 一、man指令 Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是 man 语法: m…

白嫖免费图床!CloudFlare R2太香了!

1 为啥要折腾搭建一个专属图床&#xff1f; 技术大佬写博客都用 md 格式&#xff0c;要在多平台发布&#xff0c;图片就得有外链后续如博客迁移&#xff0c;国内博客网站如掘金&#xff0c;简书&#xff0c;语雀等都做了防盗链&#xff0c;图片无法迁移 2 为啥选择CloudFlare…

对话太医管家CEO徐晶:数字化技术正在为健康管理行业带来新平衡丨数字思考者50人...

ITValue 钛媒体特别专题策划《数字思考者50人》&#xff1a;探访中国深刻的数字化思考者群体。我们理解的“TechThinker”&#xff0c;涵盖了中国数字化浪潮中的技术践行者、政策制定者与投资决策者。在这场长达10年的乘风破浪中&#xff0c;每个人都在分享技术进步的果实&…

leetcode445-Add Two Numbers II

题目 给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数字都不会以零开头。 示例1&#xff1a; 输入&#xff1a;l1 [7,2,4,3], l2 [5,6…

文件系统--软硬链接

文章目录 现象软链接硬链接 现象 建立软链接 建立硬链接 // 删除软硬链接都可以用 unlink 指令 unlink soft-link软链接 软链接是一个独立的文件&#xff0c;因为有独立的inode number 软链接的内容&#xff1a;目标文件所对应的路劲字符串如果我们直接查看软链接文件&#…

vue2vue3为什么el-table树状表格失效?

上图所示&#xff0c;后端返回字段中有hasChildren字段。 解决树状表格失效方案&#xff1a; 从后端拿到数据后&#xff0c;递归去掉该字段&#xff0c;然后就能正常显示。&#xff08;复制下方代码&#xff0c;直接用&#xff09; 亲测有效&#xff0c;vue2、vue3通用 /**…

如何运用多媒体,打造企业实力展示厅?

企业文化、产品是其长期发展的根本所在&#xff0c;为此越来越多的企业开始选择运用多媒体互动&#xff0c;来打造企业多媒体展厅的方式&#xff0c;对企业文化、品牌形象、产品进行推广宣传&#xff0c;并在多媒体互动装置的支持下&#xff0c;能让客户能够快速且全面的了解企…