I found many tutorials in internet about mutex, then after i try to build my own sample. Look at my following code snippet.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
thread := new(sync.Mutex)
y := 0
for i := 0; i < 10; i++ {
go func() {
y = i
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
time.Sleep(100000)
}
As output I've got only 10 but my target is to get 1, 2, 3, 4...10. How can I do that? Or my sample does not make any sense?
the problem is that the function closure is bound to the external scope variable i
And i
changes during the loop. When the routine executes (most likely after the loop finishes) the value of i
is 10.
There are two way of solving this, the first one is to create a new variable on each loop:
http://play.golang.org/p/ylTENWeuEl
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
or by passing the value directly to the function closure:
http://play.golang.org/p/DKd12-VNSk
for i := 0; i < 10; i++ {
go func(value int) {
thread.Lock()
fmt.Println(value)
thread.Unlock()
}(i)
}
Try :
for i := 0; i < 10; i++ {
go func(j int) {
thread.Lock()
fmt.Println(j)
thread.Unlock()
}(i)
}
Here is why.
Your example doesn't make sense. Simply write:
package main
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
fmt.Println()
}
Output:
0 1 2 3 4 5 6 7 8 9
After fixing the bugs in your program, it's easy to see that using goroutines won't provide an ordered result.
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() + 2)
thread := new(sync.Mutex)
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Print(y, " ")
thread.Unlock()
}()
}
time.Sleep(time.Second)
fmt.Println()
}
Output:
1 0 5 6 2 7 8 9 3 4
Playground:
As peterSO already pointed out, if you simply want to print in order, concurrency (Go routines) and mutexes aren't needed:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
But, if you want to do some calculation concurrently, and then print the results in the desired order, you can solve it by using a channel and have the main routine take care of the printing in order, or by using a sync.Cond
as in the example below:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
c := sync.NewCond(new(sync.Mutex))
var printed int
for i := 0; i < 10; i++ {
wg.Add(1)
go func(y int) {
// Do some costly calculation that cakes y*y%10 milliseconds
time.Sleep(time.Duration(y*y%10) * time.Millisecond)
c.L.Lock()
// Wait in a loop til it is this goroutine's turn
for printed != y {
c.Wait()
}
fmt.Printf("%d ", y)
printed++
c.L.Unlock()
// Tell the waiting routines to check if it is their turn
c.Broadcast()
wg.Done()
}(i)
}
wg.Wait()
}
Output:
0 1 2 3 4 5 6 7 8 9
Playground: http://play.golang.org/p/k7tUfKPRxW
I used sync.WaitGroup
instead of time.Sleep(time.Second)
to prevent main from exiting prematurely. It is not required for the sync.Cond
solution though.