Like most of you, I'm familiar with the fact that Go reuses the iterator variable in a for-loop, so closures for each goroutine will capture the same variable. A typical example of this the following code which will always print the final value of the loop from each goroutine:
func main() {
for i := 0; i < 5; i {
go func() {
fmt.Println(i) // prints 5 each time
}()
}
time.Sleep(100 * time.Millisecond)
}
What I haven't been able to find an explanation of, is why the goroutines do not execute until after the loop is completed. Even the following code produces the value of ii
as 10
which is set after the goroutine is called:
func main() {
for i := 0; i < 5; i {
ii := i
go func() {
fmt.Println(ii) // prints 10 each time...!
}()
ii = 10
}
time.Sleep(100 * time.Millisecond)
}
The closest thing to an explanation I've read is that for-loops typically execute faster than goroutines. This raises two questions for me: 1. Is that true? 2. Why?
CodePudding user response:
Anything is possible because the program has a data race.
Setting aside the data race, there's no evidence that the goroutines execute after the for loop is completed. The value 10 is assigned to ii
in the statement after the goroutine is started, not after the for loop.
The memory model allows for the compiler to optimize the two ii
assignments to a single assignment at the start of the for loop body. It could be that the goroutines execute immediately, but the goroutines see the value of 10 from the optimization.
CodePudding user response:
Never assume the order of execution when dealing with more than one goroutine - no matter how tempting it may be. Don't put in sleeps; don't call runtime.Gosched; don't assume anything.
The only guaranteed way to ensure order of execution is via synchronization methods such as channels, sync.Mutex
, sync.WaitGroups
etc.
CodePudding user response:
you should use the goroutine like this:
package main
import "fmt"
import "time"
func main() {
for i := 0; i < 5; i {
ii := i
go func(k int) {
fmt.Println(k) // prints 10 each time...!
}(ii)
ii = 10
}
time.Sleep(100 * time.Millisecond)
}