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()
}
You can't kill a goroutine from outside. You can signal a goroutine to stop using a channel, but there's no handle on goroutines to do any sort of meta management. Goroutines are intended to cooperatively solve problems, so killing one that is misbehaving would almost never be an adequate response.
We can close a channel in Golang with the help of the close() function. Once a channel is closed, we can't send data to it, though we can still read data from it. A closed channel denotes a case where we want to show that the work has been done on this channel, and there's no need for it to be open.
The Channel Closing Principle One general principle of using Go channels is don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders. In other words, we should only close a channel in a sender goroutine if the sender is the only sender of the channel.
You needn't close every channel when you've finished with it. It's only necessary to close a channel when it is important to tell the receiving goroutines that all data have been sent.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With