0


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

文章目录

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

一、并发不安全的

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

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

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

二、并发场景

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

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

在这里插入图片描述

三、实现 slice 并发安全

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

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

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

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

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

在这里插入图片描述

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

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

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

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

在这里插入图片描述

两种方式的比较

  加互斥锁适合于对性能要求不高的场景,毕竟锁的粒度太大,这种方式属于通过共享内存来实现通信。channle 适合于对性能要求大的场景,channle 就是专用于 goroutine 间通信的,这种方式属于通过通信来实现共享内存。

标签: golang 数据结构

本文转载自: https://blog.csdn.net/lp15929801907/article/details/130592442
版权归原作者 及尔偕老lp 所有, 如有侵权,请联系我们删除。

“Golang 中的 slice 为什么是并发不安全的?”的评论:

还没有评论