-1

I have the following code:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    ch2 := make(chan int)
    go func(c chan int, c2 chan int) {
        for {
            select {
            case v := <-c:
                fmt.Println(v)
            case v := <-c2:
                fmt.Println(v)
            default:
            }
        }
    }(ch, ch2)
    ch <- 1
    close(ch)
    close(ch2)
    time.Sleep(10 * time.Second)
}

When I run this, it prints 1 to the stdout, and then keeps printing 0. Why is this?

I know I can check whether the channel is closed in my goroutine, but I just want to know the reason for this.

Also, suppose I want to exit from a goroutine after all(multiple) channels are closed, is that possible? I was assuming that once all channels are closed, I could potentially exit from the goroutine in the default case after all channels are closed

icza
  • 389,944
  • 63
  • 907
  • 827
Ayush Gupta
  • 8,716
  • 8
  • 59
  • 92

2 Answers2

5

That's the expected behavior per Spec: Receive operator:

A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

See How does a non initialized channel behave?

If you want to terminate a loop once all values have been received from a channel (that were sent on it before it was closed), use the for ... range construct, for example:

c := make(chan int) // Initialize some channel

for v := range c {
    fmt.Println("Received:", v)
}

If you have multiple channels and you want to receive from all, you may use multiple goroutines, each having a for range for a designated channel.

Another solution is:

A function can read from multiple inputs and proceed until all are closed by multiplexing the input channels onto a single channel that's closed when all the inputs are closed. This is called fan-in.

Read more about it in The Go Blog Go Concurrency Patterns: Pipelines and cancellation: Fan-out- fain-in.

icza
  • 389,944
  • 63
  • 907
  • 827
  • so suppose I want to exit from a goroutine after all channels are closed, is that possible? I was assuming that once all channels are closed, I could potentially exit from the goroutine in the default case after all channels are closed – Ayush Gupta Oct 10 '19 at 12:39
  • @AyushGupta Simply use `for ... range`, which terminates once all values are received and the channel is closed. – icza Oct 10 '19 at 12:42
  • But that translates into multiple goroutines each with its own `for...range` loops if I want to receive from multiple channels concurrently – Ayush Gupta Oct 10 '19 at 12:43
  • 1
    This blog post is very instructive on the topic of selects on closed channels & how to leverage nil channels: https://medium.com/justforfunc/why-are-there-nil-channels-in-go-9877cc0b2308 – colm.anseo Oct 10 '19 at 12:50
  • @colminator thanks for the link! I'll check it out. – Ayush Gupta Oct 10 '19 at 12:53
3

The Go Programming Language Specification

Close

After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking.

Receive operator

A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

peterSO
  • 158,998
  • 31
  • 281
  • 276