当前位置: 首页 > news >正文

从 fatal 错误到 sync.Map:Go中 Map 的并发策略

请添加图片描述

为什么 Go 语言在多个 goroutine 同时访问和修改同一个 map 时,会报出 fatal 错误而不是 panic?我们该如何应对 map 的数据竞争问题呢?

这篇文章将带你一步步了解背后的原理,并引出解决 map 并发问题的方案。

Map 数据竞争

首先,什么是 Map 数据竞争。

当两个或多个 goroutine 在没有适当同步机制的情况下,同时访问同一块数据,且至少有一个 goroutine 在修改这块数据,就会发生数据竞争。这种情况可能导致程序的行为异常,甚至崩溃。

而 map 是 Go 中的一种常用的数据结构,提供了快速的 Key/Value 存储能力。但 Go 默认的 map 并不提供并发安全。这意味着,如果我们没有采取措施来控制 map 同步访问,如果多个 goroutine 同时对一个map进行读写操作,就可能会引发数据竞争。

Map 数据竞争产生 fatal error

在 Go 语言中,处理错误的方式通常是通过返回 Error 或者 panic。然而一旦程序检测到 map 的数据竞争,就会抛出 fatal 错误。而 fatal error 即意味会立刻崩溃。毫无疑问,Go 选择了更严格的处理方式。

通过一个简单的例子演示 fatal 错误是如何被触发的:

package mainimport ("sync"
)func main() {m := make(map[int]int)wg := sync.WaitGroup{}for i := 0; i < 10; i++ {wg.Add(1)go func() {defer wg.Done()for j := 0; j < 1000; j++ {m[j] = j}}()}wg.Wait()
}

这个例子中,我创建了一个map 类型变量 m,然后启动了10个 goroutine,每个 goroutine 都尝试向map中写入 1000 个键值对。由于 map 在 Go 中不是并发安全的,这将导致数据竞争。

这个代码可能会触发如下的 fatal 错误,输出如下所示:

fatal error: concurrent map writesgoroutine 6 [running]:... 省略exit status 2

为什么 fatal 错误, 而非 panic?

fatal 错误让我们没有在程序运行时进行补救。我猜测这背后的原因主要是以下两点:

请添加图片描述

立即暴露问题

这种处理方式确保了一旦发生数据竞争,程序将立即停止运行,迫使我们直面问题,不能逃避。

这不仅有利于我们快速发现解决并发 bug,也促使我们编写代码时,应更注重并发安全,避免发生这类问题。

虽然,这种方式可能会导致程序在运行时突然停止,但长远来看,它有助于提高程序的健壮性和可靠性。

防止数据腐败

数据竞争的后果可能非常严重,尤其是在复杂的并发系统中。

当多个goroutine不协调地访问和修改同一个map时,可能会导致map的内部状态变得不一致,甚至损坏。

这种状态的不确定性不仅会导致程序行为异常,还可能导致难以追踪的bug。

深入源码:map 并发检测

当 Go 检测到 map 的并发写入,会通过 throw 函数抛出 fatal 错误。这一过程发生在 mapassign 函数中。

以下是简化后的 mapassign 函数的伪代码,:

func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {// 检查是否有其他goroutine正在写入mapif h.flags&hashWriting != 0 {throw("concurrent map writes")}// ...其他map赋值逻辑...// 设置标志位,表示有goroutine正在写入maph.flags |= hashWriting// ...执行map的赋值逻辑...// 写入完成,清除写入标志位h.flags &^= hashWritingreturn val
}

通过这段代码,就能理解 fatal 错误是如何被触发的。对,重点就是 h.flags&hashWriting 这段条件判断。

如何避免数据竞争

在 Go 中,最常用的并发控制机制是使用 channel 或 sync 包中的工具。另外,Go 还提供了一个并发安全的 map 类型 - sync.Map。

sync.Mutex

我将通过以下这段代码演示如何使用 sync.Mutex 避免数据竞争:

package mainimport ("sync"
)func main() {m := make(map[int]int)var mu sync.Mutexwg := sync.WaitGroup{}for i := 0; i < 10; i++ {wg.Add(1)go func() {defer wg.Done()for j := 0; j < 1000; j++ {mu.Lock()m[j] = jmu.Unlock()}}()}wg.Wait()
}

在这个例子中,我们使用了 sync.Mutex 确保每次只有一个 goroutine 可以写入 map,从而避免数据竞争,保证程序的稳定性和正确性。

sync.Map

虽然 Go 的标准 map 非并发安全,但 Go 在 1.9 版本中引入了一个并发安全的 map 类型 - sync.Map,专门设计来处理并发场景下的 Key/Value 存储。

sync.Map 有一些特别的特性,它不需要显式的锁操作来保证并发安全,为它内部已经处理好了同步机制,这可简化我们的并发编程。

以下示例使用 sync.Map 改写了之前通过 sync.Mutex 实现的代码。

package mainimport ("sync""fmt"
)func main() {var sm sync.Mapwg := sync.WaitGroup{}// 写入数据for i := 0; i < 10; i++ {wg.Add(1)go func(n int) {defer wg.Done()sm.Store(n, n*n)}(i)}// 读取数据for i := 0; i < 10; i++ {wg.Add(1)go func(n int) {defer wg.Done()if value, ok := sm.Load(n); ok {fmt.Printf("Key: %v, Value: %v\n", n, value)}}(i)}wg.Wait()
}

这个例子中,sync.Map 的 Store 方法用于存储 Key/Value,Load 方法用于读取数据。我们通过这种方式就可以在多个 goroutine 中安全地使用map,而不必担心数据竞争。

另外,sync.Map 相对于传统的 sync.Mutext + map 的组合,除简化了并发编程,它还针对并发场景做了一些优化,如无锁读、细粒度锁机制(非锁整个 Map)等等。更多细节,可自行研究。

总结

在本文中,我们探讨了 Go 语言处理 map 并发操作数据竞争情况下的处理方式。这种设计突显了 Go 对并发安全的重视。另外,通过 Go 提供的 sync.Mutex 和sync.Map 等工具,可有效避免数据竞争,确保我们构建出稳定和高效的并发应用。

我想说,对这些机制的理解对于我们编写出健壮的Go程序是至关重要的。

博文地址:从 fatal 错误到 sync.Map:Go中 Map 的并发策略

相关文章:

从 fatal 错误到 sync.Map:Go中 Map 的并发策略

为什么 Go 语言在多个 goroutine 同时访问和修改同一个 map 时&#xff0c;会报出 fatal 错误而不是 panic&#xff1f;我们该如何应对 map 的数据竞争问题呢&#xff1f; 这篇文章将带你一步步了解背后的原理&#xff0c;并引出解决 map 并发问题的方案。 Map 数据竞争 首先…...

Simon算法详解

0.0 Intro 相关的算法&#xff1a; Deutsh-Jozsa算法&#xff1a; 第一个量子算法对经典算法取得指数级加速的算法 美中不足在于只能确定函数是平衡的还是非平衡的&#xff0c;无法确定函数具体的内容&#xff0c;即无法直接解出函数 Bernstein-Vazirani算法&#xff…...

jrebel IDEA 热部署

1 下载 2022.4.1 JRebel and XRebel - IntelliJ IDEs Plugin | Marketplace 2 选择下载好的zip 离线安装IDEA 插件 重启IDEA 3 打开 [Preference -> JRebel & XRebel] 菜单&#xff0c;输入 GUID address 为 https://jrebel.qekang.com/1e67ec1b-122f-4708-87d…...

pdf拆分成各个小pdf的方法

背景:由于某些缘故,一个大的pdf需要拆分成页数少的pdf,或者pdf需要去掉指定页,那么就有必要对pdf进行重新编辑,这里需要用到一个库,直接进行操作即可。 当使用Python时,可以使用PyMuPDF库来拆分PDF文件。以下是一个示例代码, import fitz # PyMuPDF def split_pdf(i…...

IntelliJ IDEA 常用快捷键一览表(通用型,提高编写速度,类结构、查找和查看源码,替换与关闭,调整格式)

文章目录 IntelliJ IDEA 常用快捷键一览表1-IDEA的日常快捷键第1组&#xff1a;通用型第2组&#xff1a;提高编写速度&#xff08;上&#xff09;第3组&#xff1a;提高编写速度&#xff08;下&#xff09;第4组&#xff1a;类结构、查找和查看源码第5组&#xff1a;查找、替换…...

MSVS C# Matlab的混合编程系列2 - 构建一个复杂(含多个M文件)的动态库:

前言: 本节我们尝试将一个有很多函数和文件的Matlab算法文件集成到C#的项目里面。 本文缩语: MT = Matlab 问题提出: 1 我们有一个比较复杂的Matlab文件: 这个MATLAB的算法,写了很多的算法函数在其他的M文件里面,这样,前面博客的方法就不够用了。会报错: 解决办法如下…...

上位机图像处理和嵌入式模块部署(qt图像处理)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多人一想到图像处理&#xff0c;本能的第一反应就是opencv&#xff0c;这也没有错。但是呢&#xff0c;这里面还是有一个问题的&#xff0c;不知…...

AI教我学编程之C#类的实例化与访问修饰符

前言 在这篇文章中&#xff0c;我将带大家深入了解C#编程语言的核心概念&#xff0c;包括类的实例化、访问修饰符的应用&#xff0c;以及C#中不同数据类型的默认值。我会通过逐步分析和具体实例&#xff0c;详细解释如何在C#中正确创建和操作对象&#xff0c;并探讨如何通过访…...

【笔记】Blender4.0建模入门-3物体的基本操作

Blender入门 ——邵发 3.1 物体的移动 演示&#xff1a; 1、选中一个物体 2、选中移动工具 3、移动 - 沿坐标轴移动 - 在坐标平面内移动 - 自由移动&#xff08;不好控制&#xff09; 选中物体&#xff1a;右上的大纲窗口&#xff0c;点击物体名称&#xff0c;物体的轮…...

一文详解 Berachain 测试网:全面介绍与教程,bitget wallet教程

什么是Berachain&#xff1f; Berachain&#xff08;web3.bitget.com/zh-CN/assets/berachain-wallet&#xff09;是一种尖端区块链技术&#xff0c;使用 Cosmos SDK 构建的 Layer-1&#xff0c;兼容以太坊虚拟机&#xff08;EVM&#xff09;。它基于一种独特的概念&#xff0c…...

小程序使用echarts图表-雷达图

本文介绍下小程序中如何使用echarts 如果是通过npm安装&#xff0c;这样是全部安装的&#xff0c;体积有点大 我这边是使用echarts中的一个组件来实现的&#xff0c;下边是具体流程&#xff0c;实际效果是没有外边的红色边框的&#xff0c;加红色边框的效果是这篇说明 1.echa…...

MacM1Pro Parallels19.1.0 CentOS7.9 Install PostgrepSQL

相关阅读 MacM1Pro安装 Parallels Desktop 19.1.0 https://blog.csdn.net/qq_41594280/article/details/135420241 MacM1Pro Parallels安装Parallels Tools https://blog.csdn.net/qq_41594280/article/details/135398780 MacM1Pro Parallels安装CentOS7.9 https://blog.csdn.n…...

Golang 中如何实现 Set

在Go编程中&#xff0c;数据结构的选择对解决问题至关重要。本文将探讨如何在 GO 中实现 set 和 bitset 两种数据结构&#xff0c;以及它们在Go中的应用场景。 Go 的数据结构 Go 内置的数据结构并不多。工作中&#xff0c;我们最常用的两种数据结构分别是 slice 和 map&#…...

记录一下uniapp 集成腾讯im特别卡(已解决)

uniapp的项目运行在微信小程序 , 安卓 , ios手机三端 , 之前这个项目集成过im,不过版本太老了,0.x的版本, 现在需要添加客服功能,所以就升级了 由于是二开 , 也为了方便 , 沿用之前的webview嵌套腾讯IM的方案 , 选用uniapp集成ui ,升级之后所有安卓用户反馈点击进去特别卡,几…...

React16源码: React中的updateHostRoot的源码实现

HostRoot 的更新 1 &#xff09;概述 HostRoot 是一个比较特殊的节点, 因为在一个react应用当中它只会有一个 HostRoot, 它对应的 Fiber 对象是我们的 RootFiber 对象重点在于它的更新过程 2 &#xff09;源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js…...

Template -- React

React 版本 Node 21.6.0Npm 10.2.4 项目 创建 npm init vite 项目名称reactjsnpm inpm run dev 依赖 npm i axios # 网络 npm i antd --save # UI npm i ant-design/icons npm i react-router-dom # 路由npm i sass -D # …...

HTML 入门手册(一)

目录 HTML 1-基础语法 单标签 双标签 整体结构 2-标题和水平线 标题 水平线 3-段落和换行 段落 换行 4-列表 无序列表 有序列表 嵌套列表 5-div和span div span 6-格式化标签 粗体 斜体 下划线中划线 上标和下标 7-超链接(a标签) 链接到URL 链接到本…...

GPT帮我快速解决工作上的问题案例

Python入门容易&#xff0c;但精通不易。自从跟着郭老师学Python后&#xff0c;工作中也想偷点懒&#xff0c;之前排班表的问题一直困扰着我&#xff0c;福音来了&#xff0c;现在随着郭老师的小蜜蜂AI出来&#xff0c;说干就干。马上来到郭老师为我们提供的AI网站&#xff1a;…...

Day32- 贪心算法part06

一、单调递增的数字 题目一&#xff1a;738. 单调递增的数字 738. 单调递增的数字 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递…...

.NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接

Flurl.Http-3.2.4 升级到 4.0.0 版本后&#xff0c;https请求异常&#xff1a;Call failed. The SSL connection could not be established. 如下图&#xff1a; Flurl.Http-3.2.4版本绕过https的代码&#xff0c;对于 Flurl.Http-4.0.0 版本来说方法不再适用&#xff0c;3.2.…...

【GitHub热门工具】TikTokDownloader深度体验:从零到一的抖音/TikTok视频下载实战

1. 为什么我们需要TikTokDownloader&#xff1f; 最近在社交媒体上看到一个超有趣的视频&#xff0c;想保存下来反复观看或者分享给朋友&#xff0c;却发现平台没有提供下载按钮&#xff1f;这种场景相信很多人都遇到过。TikTokDownloader就是为了解决这个痛点而生的开源工具&a…...

从理论到PCB:20dB耦合度的宽带定向耦合器设计全流程与性能测试

从理论到PCB&#xff1a;20dB耦合度的宽带定向耦合器设计全流程与性能测试 在射频电路设计中&#xff0c;定向耦合器作为关键的无源器件&#xff0c;其性能直接影响整个系统的信号监测、功率分配和反射测量精度。特别是工作于1-4GHz频段、耦合度为20dB的宽带定向耦合器&#xf…...

LongWriter实战教程:从零开始构建你的专属写作AI

LongWriter实战教程&#xff1a;从零开始构建你的专属写作AI 【免费下载链接】LongWriter [ICLR 2025] LongWriter: Unleashing 10,000 Word Generation from Long Context LLMs 项目地址: https://gitcode.com/gh_mirrors/lo/LongWriter LongWriter是一款基于长上下文L…...

Kubernetes集群能耗监测:RAPL与Prometheus方案对比

1. 项目概述在Kubernetes集群中实现精确的能耗监测一直是系统优化领域的难点问题。作为一名长期从事分布式系统性能调优的工程师&#xff0c;我最近完成了一项关于RAPL与Prometheus在Kubernetes集群能耗监测中的对比研究。这项研究源于我们在实际工作中遇到的一个具体问题&…...

2026年选对工作钢格板厂家,这三大核心标准决定你的采购成败

在工业厂房、化工厂、电厂等生产场景中&#xff0c;工作钢格板作为至关重要的安全承重平台与通道&#xff0c;其产品质量直接关系到人员安全与生产稳定。2026年的制造业竞争愈发激烈&#xff0c;供应链选择也更为审慎。面对市场上琳琅满目的供应商&#xff0c;您是否正为找到一…...

Auto.js开发环境搭建:用雷电模拟器替代真机,解决局域网IP不在同一网段的问题

Auto.js开发环境搭建&#xff1a;雷电模拟器网络架构深度解析与实战指南 当开发者手头没有备用安卓设备&#xff0c;或者真机与电脑的Wi-Fi连接频繁断开时&#xff0c;安卓模拟器成为了Auto.js脚本测试的救星。但模拟器的网络配置往往暗藏玄机——为什么雷电模拟器显示172.16.x…...

嘉立创拼板要求下,手把手教你用Cadence SPB17.4制作无电镀定位孔(附3D预览检查)

嘉立创拼板规范下Cadence SPB17.4无电镀定位孔全流程实战 在PCB设计领域&#xff0c;拼板工艺的规范执行直接影响生产良率。作为国内领先的PCB制造商&#xff0c;嘉立创对拼板辅助边上的定位孔有着明确的技术要求——必须采用无电镀工艺的机械孔。本文将基于Cadence SPB17.4平台…...

AI数字人驱动的矩阵内容生产:2026年技术架构与人效革命

一、背景&#xff1a;为什么2026年矩阵团队开始淘汰真人出镜&#xff1f;2024年之前&#xff0c;短视频矩阵的内容生产模式是这样的&#xff1a;环节传统方式瓶颈写脚本编剧手写1人1天最多写5条拍视频真人出镜拍摄1人1天最多拍3条剪辑剪辑师手动剪1人1天最多剪8条配音真人录音/…...

基于RP2040与CircuitPython的互动声光按钮:从硬件到代码的完整实现

1. 项目概述&#xff1a;一个能听会“说”的互动按钮几年前&#xff0c;我第一次接触嵌入式开发时&#xff0c;被那些能感知物理世界并做出回应的“智能”小玩意儿深深吸引。从简单的闪烁LED&#xff0c;到能根据环境光调整亮度的灯带&#xff0c;再到能播放声音的互动装置&…...

基于Arduino与V-USB的红外转USB键盘接收器设计与实现

1. 项目概述&#xff1a;从游戏抢答器到通用输入设备的蜕变几年前&#xff0c;我在一个教育科技展会上看到了那种用于课堂抢答的无线按钮系统&#xff0c;一套动辄上千元的价格让我这个喜欢折腾硬件的玩家直摇头。当时我就在想&#xff0c;这玩意儿的核心不就是个红外发射接收加…...