Golang 中的 map 为什么是并发不安全的?

这篇具有很好参考价值的文章主要介绍了Golang 中的 map 为什么是并发不安全的?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Golang 中的 map 为什么是并发不安全的?

一、并发不安全的

  golang 中的 map 是并发不安全的,多个 go 协程同时对同一个 map 进行读写操作时,会导致数据竞争(data race)问题,程序会 panic。

  如果一个协程正在写入 map,而另一个协程正在读取或写入 map,那么就有可能出现一些未定义的行为,例如:读取到的值可能是过期的、不正确的或 nil;写入的值可能被覆盖、丢失或者多次计数。

  go 官方认为,map 更应适配典型使用场景(不需要从多个 goroutine 中进行安全访问),而不是为了小部分情况(并发访问),导致大部分程序付出加锁的代价,影响性能,所以决定了不支持。

二、并发场景

  多个协程同时读和写,以下程序会出现致命错误:fatal error: concurrent map writes

func main() {
	s := make(map[int]int)
	for i := 0; i < 100; i++ {
		go func(i int) {
			s[i] = i
		}(i)

	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			fmt.Printf("map 的第%d个元素是%d\n", i, s[i])
		}(i)
	}
	time.Sleep(1 * time.Second)
}

golang map为什么并发不安全,golang,golang,数据结构

三、实现 map 并发安全

  如果想实现 map 并发安全,有2种方式:

方式一:使用读写互斥锁 map + sync.RWMutex

  读不用加锁;写之前调用 Lock() 函数加锁,写完之后,调用 Unlock() 解锁。

func main() {
	// 读写互斥锁
	var lock sync.RWMutex
	s := make(map[int]int)
	for i := 0; i < 100; i++ {
		go func(i int) {
			lock.Lock() // 加写锁
			s[i] = i
			lock.Unlock() // 解写锁
		}(i)

	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			fmt.Printf("map 的第%d个元素是%d\n", i, s[i])
		}(i)
	}
	time.Sleep(1 * time.Second)
}

正常打印,没有 panic。
golang map为什么并发不安全,golang,golang,数据结构

方式二:使用 go 提供的 sync.Map

  使用 Store() 函数赋值,使用 Load() 函数获取值。

func testMap2() {
	var s sync.Map
	for i := 0; i < 100; i++ {
		go func(i int) {
			s.Store(i, i)
		}(i)
	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			v, _ := s.Load(i)
			fmt.Printf("map 的第%d个元素是%d\n", i, v)
		}(i)
	}
	time.Sleep(1 * time.Second)
}

正常打印,没有 panic。

golang map为什么并发不安全,golang,golang,数据结构

两种方式的比较

  sync.Map 支持并发读写,采用空间换时间的机制,冗余了两个数据结构,分别是: read 和 dirty。

type Map struct {
	mu Mutex
	read atomic.Value
	dirty map[any]*entry
	misses int
}

  和 map+ RWLock 的实现并发的方式相比,减少了加锁对性能的影响,它做了一些优化:可以无锁访向read map,而且会优先操作read mp,如果只操作 read map 就可以满足要求,那就不用去操作write map(dirty),所以在某些特定场景中它发生锁竞争的频率会远远小于 map+RWLock 的实现方式。

  sync.map 的实现方式是通过分段锁(shard-locking)来保证并发安全性,每个分段锁只控制一部分数据,从而减少锁的竞争。虽然 sync.map 比普通的 map 更安全,但是需要注意的是,在读取和写入操作之间仍然存在风险,因此要谨慎使用。

优点:
  适合读多写少的场景。

缺点:
  写多的场景,会导致 read map 缓存失效,需要加锁,冲突变多,性能急剧下降。文章来源地址https://www.toymoban.com/news/detail-744137.html

到了这里,关于Golang 中的 map 为什么是并发不安全的?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface

    在 Go 中,如果 interface{} 作为函数参数的话,是可以传任意参数的,然后通过类型断言来转换。 举个例子: 不管是传 int 还是 string,最终都能输出正确结果。 那么,既然是这样的话,我就有一个疑问了,拿出我举一反三的能力。是否可以将 []T 转换为 []interface 呢? 比如下面

    2024年02月15日
    浏览(57)
  • Go 语言为什么不支持并发读写 map?

    大家好,我是 frank ,「 Golang 语言开发栈」公众号作者。 01 介绍 在 Go 语言项目开发中,我们经常会使用哈希表 map ,它的时间复杂度是 O(1) ,Go 语言中的 map 使用开放寻址法避免哈希碰撞。 Go 语言中的 map 并非原子操作,不支持并发读写操作。 Go 官方认为 map 在大多数情况下

    2024年02月02日
    浏览(50)
  • 【Golang】Golang进阶系列教程--为什么说 Go 语言字符串是不可变的?

    最近有读者留言说,平时在写代码的过程中,是会对字符串进行修改的,但网上都说 Go 语言字符串是不可变的,这是为什么呢? 这个问题本身并不困难,但对于新手来说确实容易产生困惑,今天就来回答一下。 首先来看看它的底层结构: 和切片的结构很像,只不过少了一个

    2024年02月14日
    浏览(47)
  • Golang对比Java、python为什么要保留指针

    平时我们在Golang使用指针一般是为了以下的情况: 方法直接修改原来对象 保证参数传递的自由,可以在传递重量级对象时使用指针 但Go 保留指针不仅仅是为了解决传递参数的问题,还跟它的语言特性有密不可分的联系。 Go 里面的变量是 值语义 ,这个跟 C/C++是一脉相承的。

    2024年01月17日
    浏览(61)
  • 【Golang】Golang进阶系列教程--为什么 Go for-range 的 value 值地址每次都一样?

    循环语句是一种常用的控制结构,在 Go 语言中,除了 for 以外,还有一个 range ,可以使用 for-range 循环迭代数组、切片、字符串、map 和 channel 这些数据类型。 但是在使用 for-range 循环迭代数组和切片的时候,是很容易出错的,甚至很多老司机一不小心都会在这里

    2024年02月15日
    浏览(50)
  • 是时候回答【我为什么要学习 Go 语言(golang)】这个问题了

    想必每个人在学习新事物之前,都会扪心自问:“我为什么要学习它呢?” 正如我们读 四大名著 一般,也只有在您读过了 四大名著 后,再细看中国几千年历史不就是 天下大势合久必分,分久必合 ,再者,便是与友数人相聚,席间您述说您通勤时所遇到有意思的事了,而您

    2023年04月09日
    浏览(44)
  • 【Golang】三分钟让你快速了解Go语言&为什么我们需要Go语言?

    博主简介: 努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:数据结构、Go,Java等相关知识。 博主主页: @是瑶瑶子啦 所属专栏: Go语言核心编程 近期目标: 写好专栏的每一篇文章 Go 语言从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目,即相关

    2023年04月21日
    浏览(52)
  • golang select两个channel性能稳定,三个channel时性能会发生抖动,为什么?

    golang select两个channel性能稳定,三个channel时性能会发生抖动,为什么? 答题思路 select — 让 Goroutine 同时等待多个 Channel 可读或者可写 — Goroutine — 调度器调度 — 资源竞争 — 不稳定、抖动 在 Go 中, select 语句用于在多个通道操作中进行选择 。当有多个通道准备好发送或接

    2024年02月20日
    浏览(39)
  • 【Golang】一篇文章带你快速了解Go语言&为什么你要学习Go语言

    目录 1. 为什么互联网世界需要Go语言 1.1 硬件限制:摩尔定律已然失效  1.2 Go语言为并发而生 1.3 Go性能强悍 1.4 Go语言简单易学 1.4.1 语法简洁 1.4.2 代码风格统一 1.4.3开发效率高  2.Go语言的诞生与发展 2.1什么是Go语言   2.2 Go语言的诞生 2.3 Go Gopher——Go语言的吉祥物 3. 为什么

    2024年02月04日
    浏览(53)
  • Golang 中的 map 详解

    1、map 的定义   在计算机科学里,被称为相关数组、map、符号表或者字典,是由一组 key, value 对组成的抽象数据结构,并且同一个 key 只会出现一次。   两个关键点:map 是由 key-value 对组成的;key 只会出现一次。   map 的设计也被称为 “The dictionary problem(字典问题)

    2024年02月14日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包