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.