在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学堂」,让知识活起来