Reputation: 3330
Say I have the following code:
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++
}
The array is a shared resource and is being read from in the loop. How do I protect the array in the loop header and do I need to? Also is it necessary to pass the array to the goroutine as a pointer?
Upvotes: 0
Views: 3977
Reputation: 10254
First you shuold know a := []int{1,2,3}
is not an array, it is a slice.
A slice literal is like an array literal without the length.
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}
Types with empty [], such as []int are actually slices, not arrays. In Go, the size of an array is part of the type, so to actually have an array you would need to have something like [16]int, and the pointer to that would be *[16]int.
Q: is it necessary to pass the array to the goroutine as a pointer?
A: No. From 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.
Upvotes: 2
Reputation: 594
First, some Go terminology:
[]int{1, 2, 3}
is a slice, not an array. An array would be written as [...]int{1, 2, 3}
.
A slice is a triplet of (start, length, capacity)
and points to an underlying array (usually heap-allocated, but this is an implementation detail that the language completely hides from you!)
Go's memory model allows any number of readers or (but not and) at most one writer to any given region in memory. The Go memory model (unfortunately) doesn't specifically call out the case of accessing multiple indices into the same slice concurrently, but it appears to be fine to do so (i.e. they are treated as distinct locations in memory, as would be expected).
So if you're just reading from it, it is not necessary to protect it at all.
If you're reading and writing to it, but the goroutines don't read and write to the same places as each other (for example, if goroutine i
only reads and writes to position i
) then you also don't need synchronization. Moreover, you could either synchronize the entire slice (which means fewer mutexes, but much higher contention) or you could synchronize the individual positions in the slice (which means much lower contention but many more mutexes and locks acquired and released).
But since Go allows functions to capture variables in scope (that is, they are closures) there's really no reason to pass the array as a pointer at all:
Your code would thus be most idiomatically be written as:
a := []int{1,2,3}
for i := 0; i < 10; i++
for i < 10 {
go func() {
for _, i := range a {
fmt.Println(a[0])
}
}()
}
I'm not really sure what the above code is supposed to be for- since it's going to print out a[0]
10 times in various goroutines, which makes it look like it's not even using the slice in a meaningful way.
Upvotes: 3