0
votes

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
Not sure what you're trying to achieve here. This program should exit prematurely as nothing is blocking the main func. I presume this isn't the intended behaviour.Chris
Investigate using waitgroups golang.org/pkg/sync/#WaitGroupChris
The program won't exit since there is a call to fmt.Scanln at the end of main function.Javad M. Amiri

1 Answers

0
votes
  1. Think what happens once all the channel-sending stuff is done: You select runs in a endless for-loop and nothing can run once the timer fired.

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.

  1. Your channels are buffered and you can send into them even without someone reading directly from them. So both statements ch1<-"hi" and `ch2<-"by2" can be executed and the select has something to select from.