I have this simplistic piece of code (or here https://play.golang.org/p/KW8_OHUp9v)
package main
import (
"fmt"
"sync"
)
func main() {
mutex := new(sync.Mutex)
for i := 1; i < 5; i++ {
for j := 1; j < 5; j++ {
mutex.Lock()
go func() {
fmt.Printf("%d + %d = %d
", i, j, j+i)
mutex.Unlock()
}()
}
}
}
It produces an output like this
1 + 2 = 3
1 + 3 = 4
1 + 4 = 5
2 + 5 = 7
2 + 2 = 4
2 + 3 = 5
2 + 4 = 6
3 + 5 = 8
3 + 2 = 5
3 + 3 = 6
3 + 4 = 7
4 + 5 = 9
4 + 2 = 6
4 + 3 = 7
4 + 4 = 8
Program exited.
Looking at the output I was surprised by few things:
j
j
I can understand lack of '1's as the variable is incremented before it is written.
Can someone explain 2. and 3. ?
You're closing over variables in a loop and then running the closure in a separate thread, while those variables continue to change. When you do this, expect the unexpected - for example, you see 5s because j
is incremented to 5 on the last iteration, which causes the loop to end, but j
still holds 5, which the separate thread can then read. It has nothing to do with your mutex; it's the cross-thread sharing of variables. If you use:
go func(i,j int) {
fmt.Printf("%d + %d = %d
", i, j, j+i)
mutex.Unlock()
}(i,j)
Then it will pass in the values of i
and j
at the time your goroutine is started, and subsequent iterations won't affect it: https://play.golang.org/p/P3kUP5e1Fp
When you execute this :
go func() {
fmt.Printf("%d + %d = %d
", i, j, j+i)
mutex.Unlock()
}()
The current goroutine makes another loop which increments j.
Incrementation takes place before printf that is why even thought the function was called whilej
was < 5
it could be increased to 5 before the function had time to print out values.
In other words you program runs like this :
Solution to this would be to pass the values by value to the function instead of sharing them throughout goroutines.