This code isn't guaranteed to work properly. You might get lucky, but apparently you've been getting unlucky. You might get lucky and the following might happen:
Let's say we have two goroutines, A and B, where A is the goroutine running main
and B is the goroutine running the anonymous function. The following might happen:
- B: Execute
select
; there's nobody writing on the quit
channel, so execute the default
case
- B: Execute
<-buffer
, so begin blocking, waiting for somebody to write to buffer
- A: Write "Go!" to
buffer
- B: Receive "Go!" and print it. Continue looping.
- A: Write true to
quit
- B: Execute
select
; A is trying to write to quit
, so execute that case. Print "Bye!" and return
- A: Since write has finished, continue and return from
main
However, this isn't guaranteed to happen. In particular, after reading from buffer
, B might keep executing, execute the select
, and fall into the default
case before A has a chance to write to quit
. That's what's probably happening, and would look like this:
- B: Execute
select
; there's nobody writing on the quit
channel, so execute the default
case
- B: Execute
<-buffer
, so begin blocking, waiting for somebody to write to buffer
- A: Write "Go!" to
buffer
- B: Receive "Go!" and print it. Continue looping
- B: Execute
select
; there's nobody writing on the quit
channel, so execute the default
case
- B: Execute
<-buffer
, so begin blocking, waiting for somebody to write to buffer
- A: Write true to
quit
, so begin blocking, waiting for somebody to read from quit
Now both A and B are blocked, and since there are no other goroutines in the system, no event can ever unblock either of them, so the system is stuck.
Solution
One way of fixing this would be to make it so that goroutine B reads from the buffer
as one of the select
cases instead of inside a select case. This way, the select
will simply block until either channel becomes available for an action, and your code will behave as you probably wanted it to:
select {
case <-quit:
fmt.Println("Bye!")
return
case str := <-buffer:
fmt.Println(str)
}
See it here on the Go Playground.
Note, however, that since the main
goroutine is returning as soon as it writes to the quit
channel, and the entire Go program exits as soon as that happens, you may (and probably will) get unlucky and fmt.Println("Bye!")
will not execute before the program quits.