Here is an exercise using channels and select in a goroutine. If the disconnect channel is changed to a buffered channel the goroutine doesn't run at all.
Why does changing from an unbuffered to a buffered channel prevent running the goroutine?
func SelectDemo(wg *sync.WaitGroup) {
messageCh := make(chan int, 10)
disconnectCh := make(chan struct{})
// go routine won't run if channel is buffered
//disconnectCh := make(chan struct{}, 1)
defer close(messageCh)
defer close(disconnectCh)
go func() {
fmt.Println(" goroutine")
wg.Add(1)
for {
select {
case v := <-messageCh:
fmt.Println(v)
case <-disconnectCh:
fmt.Println(" disconnectCh")
// empty the buffered channel before exiting
for {
select {
case v := <-messageCh:
fmt.Println(v)
default:
fmt.Println(" disconnection, return")
wg.Done()
return
}
}
}
}
}()
fmt.Println("Sending ints")
for i := 0; i < 10; i++ {
messageCh <- i
}
fmt.Println("Sending done")
disconnectCh <- struct{}{}
}
Here's the code to call the function from main. I use the wait group to assure that the goroutine completes before the program exits:
wg := sync.WaitGroup{}
ch09.SelectDemo(&wg)
wg.Wait()
That code logic has many flaws - some of them are:
1- Since the messageCh
is buffered, this code is not blocking:
for i := 0; i < 10; i++ {
messageCh <- i
}
so the next code is in the fast path to run:
disconnectCh <- struct{}{}
if you make the disconnectCh
buffered, this line runs without blocking too, and the SelectDemo
function may exit befor running the wg.Add(1)
.
So: You must put:
wg.Add(1)
before
go func() {
2- Even with wg.Add(1)
before go func() {
code -
you have:
defer close(messageCh)
defer close(disconnectCh)
which will close both channels at SelectDemo
function return
And this select
is a random selection since both channels are ready:
fmt.Println(" goroutine")
for {
select {
case v := <-messageCh:
fmt.Println(v)
case <-disconnectCh:
and it is highly likely that the second select:
for {
select {
case v := <-messageCh:
fmt.Println(v)
default:
fmt.Println(" disconnection, return")
wg.Done()
return
}
}
will run forever since the messageCh
is closed, returning 0
forever after channel data read:
case v := <-messageCh:
fmt.Println(v)