I use this piece of code for playing with goroutines for the sake of learning. There are different things that I cannot get:
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string) // <A>
ch2 := make(chan string) // <B>
go test(ch1, ch2)
ch1 <- "hi" // <C>
ch2 <- "bye" // <D>
fmt.Scanln()
}
func test(ch1 chan string, ch2 chan string) {
timeout := time.After(time.Second * 2) // <E>
defer func() { fmt.Println("returning") }()
for {
select {
case s := <-ch1:
fmt.Println(s, "1")
ch2 <- "ch2"
fmt.Println("after 1")
case s := <-ch2:
fmt.Println(s, "2")
ch1 <- "ch1"
fmt.Println("after 2")
case <-timeout:
fmt.Println("timed out")
return
}
}
}
If I run the code as is, I always get:
hi 1
fatal error: all goroutines are asleep - deadlock!
The point is the program exactly waits for the specified duration in part E. I mean if I add to sleep time the fatal error comes after elapsing that time. So first question is:
1- What exactly happens? Can anyone explain the behavior?
1-1- Why the code always prints "hi 1"
? I have read that select statement picks from ready channels randomly, so why always "hi 1"
? If I swap C and D then it always prints "bye 2"
.
1-2- Why program waits for that period of time and then the deadlock happens?
Now suppose I make channels buffered in A and B with size of 1 i.e.:
ch1 := make(chan string, 1) // <A>
ch2 := make(chan string, 1) // <B>
Now each time I run the program it randomly prints either of "hi 1"
and "bye 2"
(just one time) and waits forever (if I hit enter program exits, as coded in main function)
2- What happens now? Please explain.
Finally if I make buffer size 2 (or more):
ch1 := make(chan string, 2) // <A>
ch2 := make(chan string, 2) // <B>
program runs as expected and prints both "hi 1"
and "bye 2"
one after another until time coded in E section elapsed:
ch1 1
after 1
ch2 2
after 2
ch1 1
after 1
ch1 1
after 1
ch2 2
.
.
.
timed out
returning
I think here everything is clear and since channels are buffered with an apt size everything works as expected.
1.1. You send on ch1 first. The select loop selects randomly from all path which can run. The second case cannot run as there is nothing in ch2 until you got "hi" through ch1.
1.2. Your timeout.
ch1<-"hi"
and `ch2<-"by2" can be executed and the select has something to select from.