I have a question about go routines.
My code:
func main() {
ok := make(chan bool, 1)
i := 0
fmt.Println("Starting...")
for i <= 3 {
fmt.Println("Loop: ", i)
go long(ok, i)
time.Sleep(1 * time.Second)
i = i + 1
select {
case _ = <-ok:
default:
fmt.Println("Default")
}
}
fmt.Println("Done...")
}
func long(c chan bool, i int){
fmt.Println("Inside long: ", i)
time.Sleep(3 * time.Second)
fmt.Println("Done with loop: ", i)
c <- true
}
This gives me output:
Starting...
Loop: 0
Inside long: 0
Default
Loop: 1
Inside long: 1
Default
Loop: 2
Inside long: 2
Done with loop: 0
Loop: 3
Inside long: 3
Done with loop: 1
Done...
Because I use the default
in the select
, the channel is non blocking. And the main function exits and so do all current routines. I then read about sync
and WaitGrops
.
func main() {
ok := make(chan bool, 1)
var wg sync.WaitGroup
i := 0
fmt.Println("Starting...")
for i <= 3 {
fmt.Println("Loop: ", i)
wg.Add(1)
go long(ok, i)
time.Sleep(1 * time.Second)
i = i + 1
select {
case _ = <-ok:
default:
fmt.Println("Default")
}
}
wg.Wait()
fmt.Println("Done...")
}
Which gives me:
Starting...
Loop: 0
Inside long: 0
Default
Loop: 1
Inside long: 1
Default
Loop: 2
Inside long: 2
Done with loop: 0
Loop: 3
Inside long: 3
Done with loop: 1
Done with loop: 2
Done with loop: 3
We are now closer to my desired execution which is: The for loop makes all it calls to the function, and then I get the result async. This would be wonderful if it all worked. But an error is generated:
fatal error: all goroutines are asleep - deadlock!
Why is that and how should I fix it? (Is it possible to fix without knowing how many times wg.Add() will be executed?)
So, bits' answer removes the channel which fixes your problem.
The reason the channel is a problem is that your goroutines try to write into it three times and it has a buffer size of one. And your main routine quickly rips off three goroutines and never waits to read the channel because of the default in select. So it never gets read and the goroutines cannot write to it.
You need to call Done()
on WaitGroup
as many times as you call Add(1)
, so that wg.Wait()
can unblock. You don't need channel to synchronize anymore:
package main
import (
"sync"
"fmt"
"time"
)
func main() {
var wg sync.WaitGroup
i := 0
fmt.Println("Starting...")
for i <= 3 {
fmt.Println("Loop: ", i)
wg.Add(1)
go long(&wg, i)
time.Sleep(1 * time.Second)
i = i + 1
}
wg.Wait()
fmt.Println("Done...")
}
func long(wg *sync.WaitGroup, i int){
fmt.Println("Inside long: ", i)
time.Sleep(3 * time.Second)
fmt.Println("Done with loop: ", i)
wg.Done()
}
You can do this with channels as well, but it's not as elegant as with WaitGroup
, because it requires a bit more synchronisation effort.
For your problem above, you can do something like this:
package main
func main() {
ok := make(chan bool, 1)
done := make(chan bool)
i := 0
j := 0
fmt.Println("Starting...")
for i <= 3 {
fmt.Println("Loop: ", i)
go long(ok, i)
time.Sleep(1 * time.Second)
i = i + 1
}
go func() {
for {
select {
case _ = <-ok:
j++
if j == 4 {
done <- true
return
}
}
}
}()
<-done
fmt.Println("Done...")
}
func long(c chan bool, i int) {
fmt.Println("Inside long: ", i)
time.Sleep(3 * time.Second)
fmt.Println("Done with loop: ", i)
c <- true
}