从 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 的并发策略

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

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

相关文章

node介绍

1.node是什么 Node是一个基于Chrome V8引擎的JS运行环境。 Node不是一个独立的语言、node不是JS框架。 Node是一个除了浏览器之外的、可以让JS运行的环境 Node.js是一个让JS运行在服务端的开发平台&#xff0c;是使用事件驱动&#xff0c;异步非阻塞I/O&#xff0c;单线程&…

【SpringBoot技术专题】「开发实战系列」Undertow web容器的入门实战及调优方案精讲

Undertow web容器的入门实战及调优方案精讲 Undertow web容器Undertow 介绍官网API给出一句话概述Undertow&#xff1a;官网API总结特点&#xff1a;Lightweight&#xff08;轻量级&#xff09;HTTP Upgrade Support&#xff08;支持http升级&#xff09;、HTTP/2 Support支持H…

提升网站关键词排名的工具

随着互联网的蓬勃发展&#xff0c;网站的关键词排名成为衡量网站流量和曝光度的重要指标。在这个竞争激烈的数字时代&#xff0c;站在搜索引擎结果的前列变得至关重要。为了实现这一目标&#xff0c;合理利用关键词排名优化工具是必不可少的。本文将重点介绍147SEO软件&#xf…

《WebKit 技术内幕》之八(1):硬件加速机制

《WebKit 技术内幕》之八&#xff08;1&#xff09;&#xff1a;硬件加速机制 1 硬件加速基础 1.1 概念 这里说的硬件加速技术是指使用GPU的硬件能力来帮助渲染网页&#xff0c;因为GPU的作用主要是用来绘制3D图形并且性能特别好&#xff0c;这是它的专长所在&#xff0c;它…

腾讯云tsf平台-部署微服务项目

腾讯云tsf平台-部署微服务项目 一、腾讯云tsf平台简介二、部署准备0&#xff08;数据库、中间件等部署&#xff09;三、部署准备1&#xff08;创建集群和命名空间&#xff09;1、准备部署资源--集群2、使用容器部署微服务步骤 1&#xff1a;创建容器集群步骤 2&#xff1a;创建…

canvas绘制六芒星

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

计算机网络——运输层(2)暨小程送书

计算机网络——运输层&#xff08;2&#xff09;暨小程送书 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 运输层&#xff08;2&#xff09;TCP/IP对比TCP&#xff08;传输控制协议&#xff09;&#xff1a;IP&#xff08;互联网协议&#xff09;&#xff1a;总结 拥塞…

一.初识Linux 1-3操作系统概述Linux初识虚拟机介绍

目录 一.初识Linux 1.操作系统概述 计算机组成 硬件&#xff1a; 软件&#xff1a; 操作系统&#xff1a; 操作系统工作流程 操作系统作用 常见的操作系统 PC端&#xff1a; 移动端&#xff1a;&#xff08;掌上操作系统&#xff09; 一.初识Linux 2.Linux初识 linu…

【微信小程序独立开发 5】后端搭建联调

前言&#xff1a;上节我们完成了个人信息页的编写&#xff0c;本节完成将个人信息发给后端&#xff0c;并由后端存储 创建Spring Boot项目 配置maven仓库 使用自己下载的maven版本 添加pom文件 <dependencies><dependency><groupId>org.springframework.boo…

【服务器】Xshell与Xftp软件的使用指南

目录 【Xshell软件】 1.1 Xshell软件的功能 1.2 Xshell软件的使用 【Xftp软件】 2.1 Xftp软件的功能 2.2 Xftp软件的使用 可替代产品【FinalShell】 3.1 FinalShell软件的使用 3.2 FinalShell连接服务器失败解决方法 可替代产品【FileZilla】

Java网络编程——UDP通信原理

一、TCP和UDP概述 传输层通常以TCP和UDP协议来控制端点与端点的通信 TCPUDP协议名称传输控制协议用户数据包协议是否连接面向连接的协议。数据必须要建立连接无连接的协议&#xff0c;每个数据报中都给出完整的地址信息&#xff0c;因此不需要事先建立发送方和接受方的连接是…

Java面试题50道

文章目录 1.谈谈你对Spring的理解2.Spring的常用注解有哪些3.Spring中的bean线程安全吗4.Spring中的设计模式有哪些5.Spring事务传播行为有几种6.Spring是怎么解决循环依赖的7.SpringBoot自动配置原理8.SpringBoot配置文件类型以及加载顺序9.SpringCloud的常用组件有哪些10.说一…

toad 库中 iv 计算逻辑

merge 函数默认为决策树合并 决策树叶子结点默认值DEFAULT_BINS 10&#xff0c;可通过 n_bins 人为设置叶子结点数

Eureka整合seata分布式事务

文章目录 一、分布式事务存在的问题二、分布式事务理论三、认识SeataSeata分布式事务解决方案1、XA模式2、AT模式3、SAGA模式4.SAGA模式优缺点&#xff1a;5.四种模式对比 四、微服务整合Seata AT案例Seata配置微服务整合2.1、父工程项目创建引入依赖 2.2、Eureka集群搭建2.3、…

19万9的小米SU7已经彻底被否了

文 | AUTO芯球 作者 | 李诞 雷总 您是真不听劝啊 还要准备和米粉“干一架”啊 我和大家一样啊 这下好了 19万9的小米SU7已经彻底被否了 说实话 我心理真不是滋味 毕竟大家都说了&#xff0c; 9.9是雷爹&#xff0c;9万9是雷帝&#xff0c;15w是雷神 19万是雷总&#x…

Java开发工具:IntelliJ IDEA 2023 for Mac中文激活

IntelliJ IDEA 2023是一款由JetBrains开发的强大的集成开发环境&#xff08;IDE&#xff09;软件&#xff0c;适用于多个编程语言。它旨在提高开发人员的生产力和代码质量。 软件下载&#xff1a;Java开发工具&#xff1a;IntelliJ IDEA 2023 for Mac中文激活 IntelliJ IDEA 20…

[Java面试]JavaSE知识回顾

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理Java面试中JavaSE中会涉及到的知识点 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一下&#x…

设计螺栓长度的基本原理和细节考虑——SunTorque智能扭矩系统

螺栓长度通常是指从螺栓头部到底部的整体长度。在选择或设计螺栓长度时&#xff0c;需要考虑多个因素&#xff0c;如被连接件的材料、厚度、螺栓间距以及预紧力等。设计螺栓长度应注意哪些原则和细节&#xff0c;SunTorque智能扭矩系统和大家一起来了解。设计螺栓长度的基本原则…

每日一题——LeetCode1304.和为零的N个不同整数

方法一 个人方法 n为偶数&#xff0c;只要同时放入一个数的正数和负数&#xff0c;那么和总为0&#xff0c;n是奇数就放入一个0&#xff0c;剩下的当偶数看待 var sumZero function(n) {let res[]if(n%2!0){res.push(0)n--}nn/2for(let i1;i<n;i){res.push(i)res.push(-i…

Sci Transl Med | 自体中和抗体在早期抗逆转录病毒治疗中增加

今天给同学们分享一篇实验文章“Autologous neutralizing antibodies increase with early antiretroviral therapy and shape HIV rebound after treatment interruption”&#xff0c;这篇文章发表在Sci Transl Med期刊上&#xff0c;影响因子为17.1。 结果解读&#xff1a; …