Hi guys, I have a great demonstration on why an un-cached channel might malfunction while receiving:
package main
import(
"fmt"
"time"
)
func main(){
t := time.NewTicker(time.Second * 3)
stopnow := make(chan bool)
//stopnow := make(chan bool, 1) //cached channel instead
var n int
for{
select{
case <-t.C:
n++
fmt.Printf("%d\n", n)
if n==3{
stopnow <- true //The Only Sending!!!
t.Stop()
}
case a:=<-stopnow: //The Only Receiving!!!
if a{
goto END
}
}
}
END:
fmt.Println("out of select")
}
In the code above, you will never see the printout
"out of select"
, because the for-select receiver is not working while the code is run inside timer receiver in the line "
stopnow <- true".
So an un-cached channel like "
stopnow := make(chan bool)" will occasionally but inevitably miss receiving while the code is busy processing a competing receiving.
That is why I use cached channel instead, like
"
stopnow := make(chan bool, 1)", which un-received message(s) will be cached until gotten received.
However there comes another vulnerability - How large should the cache be?
In a simple scenario like this, I am very confident there will be 1 message to receive only. But on a complex server, it is very common that a goroutine will receive unexpected many messages from other goroutine or functions. And it is not safe for the programmer to just guess a ceiling for the number of unprocessed messages on any time - just as to guess the maximum on how many cars can line up after a red light. If you guess wrong, only one additional message will breach the cache and cause a crash:
package main
var c = make(chan int, 1)
func main() {
c <- 1
c <- 2 //fatal error: all goroutines are asleep - deadlock!
c <- 3
}
The code above crashes at the point that the cache of channel c is full, but the sender still puts another message into it.
What is your thought on this?
Regards,
Zhaoxun