I'm new to Go and I'm trying to learn about the concurrency patterns. When I run the following code, I sometimes get the expected results (a full array of numbers from 0 to 9999). Other times I just get a "That's it" message with the time displayed. And sometimes I just get a "Sending on closed channel" error. What could be going wrong here?
package main
import (
"fmt"
"time"
"sync"
)
func JobsDispatcher(in chan int, data []int){
for _, value := range data{
in<-value
}
close(in)
}
func Worker(in chan int, out chan int, wg *sync.WaitGroup){
wg.Add(1)
for{
inMsg, ok := <-in
if !ok{
wg.Done()
return
}
out <- inMsg
}
}
func PrintInt(out chan int){
for {
outMsg, ok := <-out
if !ok{
fmt.Println("")
fmt.Println("That's it")
return
}
fmt.Println(outMsg)
}
}
func ParallelPrint(data []int){
var wg sync.WaitGroup
in := make(chan int)
out := make(chan int)
parallelStartTime := time.Now()
go JobsDispatcher(in, data)
for i:=0;i<5;i++{
go Worker(in,out,&wg)
}
go func(){
wg.Wait()
close(out)
}()
PrintInt(out)
fmt.Println(time.Since(parallelStartTime))
}
func main(){
data := make([]int,0)
for i:=0;i<10000;i++{
data = append(data, i)
}
ParallelPrint(data)
}
This one is easy. This is why you never use WaitGroup's Add in a goroutine. Always call it before starting a goroutine.
The problem is that you stack up a bunch of goroutine's and then call Wait immediately. Go does not promise to run your goroutines at any particular time, just like POSIX or Windows threads are not guaranteed.
So, in this case, you gave the scheduler a bunch of goroutines to run in the future, but it decided to finish your code first. So it ran wg.Wait()
and close(out)
before ever doing wg.Add()
.