Golang互斥量在goroutines中的共享数组范围内

时间:2017-02-14 23:56:13

标签: arrays go concurrency locking mutex

说我有以下代码:

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吗?

2 个答案:

答案 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

  

如果函数采用切片参数,则更改它对元素的影响   对于调用者来说,切片是可见的,类似于传递一个   指向底层数组的指针。