说我有以下代码:
a := []int{1,2,3}
i := 0
var mu = &sync.Mutex{}
for i < 10 {
go func(a *[]int) {
for _, i := range a {
mu.Lock()
fmt.Println(a[0])
mu.Unlock()
}
}(&a)
i++
}
该数组是一个共享资源,正在循环中读取。如何保护循环头中的数组,是否需要?还有必要将数组作为指针传递给goroutine吗?
答案 0 :(得分:3)
首先,一些Go术语:
[]int{1, 2, 3}
是切片,而不是数组。数组将写为[...]int{1, 2, 3}
。
切片是(start, length, capacity)
的三元组并指向底层数组(通常是堆分配的,但这是语言完全隐藏的实现细节!)
Go的内存模型允许任意数量的读者或(但不是和)最多一个作者到内存中的任何给定区域。 Go memory model(遗憾的是)并没有特别指出同时访问同一切片的多个索引的情况,但这样做似乎没问题(即它们被视为内存中的不同位置,如会被期待的。)
因此,如果您只是从中阅读,则根本不需要保护它。
如果您正在阅读和,但goroutines不会相互读取和写入相同的位置(例如,如果goroutine {{1} }只读取和写入位置i
)然后您也不需要同步。此外,您可以同步整个切片(这意味着更少的互斥体,但更多更高的争用)或者您可以同步切片中的各个位置(这意味着更低的争用但更多的互斥锁和锁获取并释放)。
但是由于Go允许函数捕获作用域中的变量(也就是说它们是闭包),所以根本没有理由将数组作为指针传递:
因此,您的代码最常被写为:
i
我不确定上面的代码应该是什么 - 因为它会在各种goroutines中打印出a := []int{1,2,3}
for i := 0; i < 10; i++
for i < 10 {
go func() {
for _, i := range a {
fmt.Println(a[0])
}
}()
}
10次,这使它看起来像是&#39 ;甚至没有以有意义的方式使用切片。
答案 1 :(得分:2)
首先,您知道a := []int{1,2,3}
不是数组,而是切片。
切片文字就像没有长度的数组文字。
这是一个数组文字:
[3]bool{true, true, false}
这会创建与上面相同的数组,然后构建一个切片 引用它:
[]bool{true, true, false}
带有[]的类型,例如[] int实际上是切片,而不是数组。在Go中,数组的大小是类型的一部分,所以要实际拥有一个数组,你需要有类似[16] int的东西,而指向它的指针将是* [16] int。
问:是否有必要将数组作为指针传递给goroutine?
答:不可以。https://golang.org/doc/effective_go.html#slices
如果函数采用切片参数,则更改它对元素的影响 对于调用者来说,切片是可见的,类似于传递一个 指向底层数组的指针。