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

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

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

一、并发不安全的

  在Go语言中,slice是并发不安全的,主要有以下两个原因:数据竞争、内存重分配。

  数据竞争:slice底层的结构体包含一个指向底层数组的指针和该数组的长度,当多个协程并发访问同一个slice时,有可能会出现数据竞争的问题。例如,一个协程在修改slice的长度,而另一个协程同时在读取或修改slice的内容。

  内存重分配:在向slice中追加元素时,可能会触发slice的扩容操作,在这个过程中,如果有其他协程访问了slice,就会导致指向底层数组的指针出现异常。

二、并发场景

  多个协程同时向 slice 追加元素,会有一部分元素被追加到了旧的底层数组里,最终 slice 的长度小于目标值。

func main() {
	a := make([]int, 0)
	for i := 0; i < 10000; i++ {
		go func(i int) {
			a = append(a, i)
		}(i)
	}
	fmt.Println(len(a)) // 9015 < 10000
}

golang slice 并发安全,golang,golang,数据结构

三、实现 slice 并发安全

  要实现 slice 并发安全,有两种方法:加互斥锁、使用channel串行化操作

方式一:使用互斥锁 sync.Mutex

  追加元素之前调用 Lock() 函数加锁,追加完后,调用 Unlock() 解锁。

func main() {
	var lock sync.Mutex //互斥锁
	a := make([]int, 0)
	var wg sync.WaitGroup
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			lock.Lock()
			defer lock.Unlock()
			a = append(a, i)
		}(i)
	}
	wg.Wait()
	fmt.Println(len(a))
	// equal 10000
}

最终 slice 的长度等于目标值。

golang slice 并发安全,golang,golang,数据结构

方式二:使用channel串行化操作

  生产者生产元素,发送到通道中,消费者从通道中接收元素,追加到 slice 中。使用无缓冲通道,接收方、发送方必须同时存在,负责任意一方都会阻塞。

func main() {
	buffer := make(chan int)
	a := make([]int, 0)
	// 消费者
	go func() {
		for v := range buffer {
			a = append(a, v)
		}
	}()
	// 生产者
	var wg sync.WaitGroup
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			buffer <- i
		}(i)
	}
	wg.Wait()
	fmt.Println(len(a))
	// equal 10000
}

最终 slice 的长度等于目标值。

golang slice 并发安全,golang,golang,数据结构

两种方式的比较

  加互斥锁适合于对性能要求不高的场景,毕竟锁的粒度太大,这种方式属于通过共享内存来实现通信。channle 适合于对性能要求大的场景,channle 就是专用于 goroutine 间通信的,这种方式属于通过通信来实现共享内存。文章来源地址https://www.toymoban.com/news/detail-753337.html

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

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

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

相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2024年02月20日
    浏览(48)
  • 【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日
    浏览(65)
  • Golang 中的数组Array以及Slice底层实现

    数组是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素 (element),这种类型可以是任意的原始类型,比如 int 、 string 等,也可以是用户自定义的类型。一个数组包含的元素个数被称为数组的长度。 在 Golang 中数组是一个长度固定的数据类型, 数组的长度

    2024年02月16日
    浏览(46)
  • golang并发安全-select

    前面说了golang的channel, 今天我们看看golang select 是怎么实现的。 select 非默认的case 中都是处理channel 的 接受和发送,所有scase 结构体中c是用来存储select 的case中使用的channel 编译器在中间代码生成期间会根据 select 中 case 的不同对控制语句进行优化,这一过程都发生在cmd/c

    2024年01月23日
    浏览(48)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包