Golang 互斥范围遍及 goroutine 中的共享数组

Golang mutex ranging over shared array in goroutines

假设我有以下代码:

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++
}

数组是共享资源,正在循环读取。如何保护循环 header 中的数组,我是否需要这样做?还需要将数组作为指针传递给goroutine吗?

首先,一些 Go 术语:

[]int{1, 2, 3} 是一个 切片 ,而不是一个 数组 。数组将写为 [...]int{1, 2, 3}.

切片是 (start, length, capacity) 的三元组并指向底层数组(通常是 heap-allocated,但这是语言完全向您隐藏的实现细节!)

Go 的内存模型允许任意数量的读取器或(但不是和)至多一个写入器到内存中的任何给定区域。 Go memory model(不幸的是)并没有特别指出同时访问同一个切片中的多个索引的情况,但这样做似乎很好(即它们被视为内存中的不同位置,就像预期)。

所以如果你只是从中读取,根本没有必要保护它。

如果你正在读 并且 写入它,但是 goroutines 不在彼此相同的地方读写(例如,如果 goroutine i 只读取和写入位置 i) 然后你也不需要同步。此外,您可以同步整个切片(这意味着更少的互斥锁,但 更高的争用)或者您可以同步切片中的各个位置(这意味着更低的争用但更多的互斥锁以及获取和释放的锁)。

但是由于 Go 允许函数在范围内捕获变量(也就是说,它们是闭包),所以根本没有理由将数组作为指针传递:

因此,您的代码最惯用的写法是:

a := []int{1,2,3}
for i := 0; i < 10; i++ 
for i < 10 {
    go func() {
        for _, i := range a {
            fmt.Println(a[0])
        }
    }()
}

我不太确定上面的代码应该用于什么——因为它将在各种 goroutine 中打印出 a[0] 10 次,这使得它看起来甚至没有使用 slice一种有意义的方式。

首先你应该知道 a := []int{1,2,3} 不是一个数组,它是一个 slice.

切片文字就像没有长度的数组文字。

This is an array literal:

[3]bool{true, true, false}

And this creates the same array as above, then builds a slice that references it:

[]bool{true, true, false}

[]为空的类型,比如[]int,其实是切片,不是数组。在 Go 中,数组的大小是类型的一部分,因此要真正拥有一个数组,您需要有类似 [16]int 的东西,指向它的指针将是 *[16]int.

问:是否需要将数组作为指针传递给goroutine?

答:号从https://golang.org/doc/effective_go.html#slices

If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array.