package main
import "fmt"
func main() {
completed := make(chan bool, 2)
m := map[string]string{"a": "a", "b": "b"}
for k, v := range m {
go func() {
fmt.Println(k, v)
completed <- true
}()
}
<- completed
<- completed
}
I ran the code hundreds of times, the output is always:
b b
b b
However, I have never seen pair a a printed. Is this some sort of weird concurrency issue?
This is a classic example of a "Race on counter loop". If you run your code with go run -race I suspect it will tell you that.
The following will do what you expect:
func main() {
completed := make(chan bool, 2)
m := map[string]string{"a": "a", "b": "b"}
for k, v := range m {
go func(k, v string) {
fmt.Println(k, v)
completed <- true
}(k, v)
}
<- completed
<- completed
}
Your original code is likely to print only b's (or only a's), on any machine, and in fact it happens on the Go playground: http://play.golang.org/p/Orgn030Yfr
This is because the anonymous function is referring to the variables from the for k, v line, not the values that those variables happen to have at the moment the goroutine is created. First both variables are set to one value, and one goroutine is spawned, then they're set to the other value, and another goroutine is spawned. Then, both goroutines run, and they both see the newest values of k and v. By the way, this isn't really specific to multithreading or to Go (play.golang.org runs everything in a single thread and still shows this "bug.") This same problem happens in JavaScript where there is guaranteed to be only one thread:
obj = {a: 'a', b: 'b'};
for (k in obj) {
setTimeout(function() { console.log(k, obj[k]); }, 0);
}
by the time the anonymous function runs, the for loop has finished, so 'k' is left with its most recent value for both runs of the function.
You are passing no arguments to the goroutines. They're thus both using the same instances of k and v so as any value they have, in your case, after the range loop terminated. With GOMAXPROCS > 1, you moreover have a data race on those variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With