I am just starting to learn the Go language and have reached the lesson about concurrency.
I have invented a small task for myself to try to implement what I have learned about closing goroutines.
If we close channel it's case
will always be selected in the select
statement, which is a great way to broadcast cancelation signal to all goroutines.
Below I have 2 goroutines and a quit
channel that never gets received.
Code timeouts on Playground.
If I comment out default
part in the goroutines then quit
signal gets received.
I really do not understand why is this happening and how to fix it, although I am trying.
Can somebody please explain me what is the problem and offer some advice on how to solve it?
package main
import (
"fmt"
"sync"
"time"
)
func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := 1
for {
select {
case <-quit:
fmt.Println("[+]Quiting...")
return
default:
fmt.Printf("%v ", i)
i++
}
}
}
func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
i := -1
for {
select {
case <-quit:
fmt.Println("[-]Quiting...")
return
default:
fmt.Printf("%v ", i)
i--
}
}
}
func main() {
quit := make(chan struct{})
wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
wg.Add(2)
go positive_numbers(quit, &wg)
go negative_numbers(quit, &wg)
go func(quit chan struct{}) {
defer close(quit)
time.Sleep(1 * time.Second)
}(quit)
wg.Wait()
}
This code works alright in real life; it's just not compatible with the playground's "fake time" because positive_numbers
and negative_numbers
don't block, and the runtime can't decide how many iterations of each get to run before the Sleep
expires (basically, the prints take zero emulated wallclock time, so they try to produce infinite output, and you hit the playground CPU usage limit without ever advancing the wallclock time at all). On real machines, where you can only print finite output in finite time, you get reasonable behavior.
In the playground, if you add something like time.Sleep(time.Millisecond)
after each print, you force the fake clock to move forward, and the program also terminates.