Go错误集锦 | append操作造成数据竞争

2023-01-31 16:11:28 浏览数 (1)

在Go中,使用append给切片中添加元素是常见的操作。下面我们看一个使用append引起的数据竞争的例子。

在下面的示例中,我们创建一个长度为1的切片,然后通过两个协程使用append往该切片中添加元素,如下:

代码语言:javascript复制
s := make([]int, 1)

go func() {
    s1 := append(s, 1)
    fmt.Println(s1)
}()

go func() {
    s2 := append(s, 2)
    fmt.Println(s2)
}()

在该示例中,会产生数据竞争吗?答案是不会。我们看下为什么。

首先,我们回顾一下slice的基础知识。slice结构的底层是一个数组,另外还有两个属性:长度length和容量cap。长度是指的切片中当前有多少个元素。容量是指底层的数组能容纳的总的元素个数。当使用append操作时,其行为依赖于该slice是否满了,即length是否和cap相等。如果slice已经满了,那么append的操作将会重新分配一个更大空间的新数组,然后将原数组中的元素拷贝过来,再将新元素加在新数组中。如果slice没满,那么append的操作是将新元素直接加到已存在的数组中。

在该示例中,我们创建的切片s的长度和容量都是1,即该切片是满的状态。所以在协程中使用append操作,则各自都会创建一个新的数组,并将新元素添加到自己的新数组中,两个数组空间互不影响。所以不存在是数据竞争的问题。

那如果我们将初始化切片的语句变更成如下这样:

代码语言:javascript复制
s := make([]int, 0, 1)

同样还是在两个协程中使用append操作给s中添加新的元素,那这样就会导致数据竞争。因为在创建切片s的时候其长度为0,但容量是1,说明该切片是未满的状态,还有空间可以存放数据,所以在协程中使用append进行操作时,实际上是在争抢s[0]的位置。

好了,以上就是今天我们要分享的知识点,希望大家在今后的研发中避免踩同样的坑。


欢迎关注「Go学堂」,让知识活起来

0 人点赞