Should I consume all of a golang channel's values if I know they are finite?

Issue

I’m working through the Concurrency section of A Tour of Go, and I’m curious about proper Go convention for consuming finite channels. In this exercise, I need to read values from two channels and determine if the values are the same and in the same order. If not, I can immediately return false from my method. However, if I do that, will Go clean up my channels for me automatically, or will my goroutines be left hanging forever and consuming resources?

The best way to handle this would be to pass a cancel channel into my goroutines, but since the goroutines read a finite amount of data, it seems fine to just consume all the data. What is the best way to handle this case in real life?

Solution

Using quit channels,
as discussed in the
Go Concurrency Patterns: Pipelines and cancellation
blog article
and Heath Borders’ answer,
is often a good idea.

There is also the
golang.org/x/net/context package
discussed in the
Go Concurrency Patterns: Context
blog article which adds timeouts, deadlines, and other features.

However, to directly address:

will Go clean up my channels for me automatically

It depends on the channel buffering and how the channels are written to.

E.g.

func writeValues(n int, c chan int) {
    for i := 0; i < n; i++ {
        c <- i
    }
    log.Println("writeValues", n, "done")
}

func main() {
    ch1 := make(chan int, 12)
    ch2 := make(chan int, 6)
    ch3 := make(chan int)

    go writeValues(10, ch1)
    go writeValues(11, ch2)
    go writeValues(12, ch3)

    time.Sleep(time.Second) // XXX
}

Playground

Here the first goroutine will complete and ch1 (and anything buffered in it) would be garbage collected and cleaned up.
However, the later two goroutines will block waiting until they can write all their values. The garbage collector would never touch ch2 and ch3 since the blocked goroutines keep a reference to them.
Note ch2 would get cleaned up if as few as five items where read from the channel.

Usually, you only rely on this when doing something like:

    errc := make(chan error, 1)
    go func() { errc <- someErrorReturningFunc() }()

If the function being called has no way to cancel it, then this is a common idiom.
You can do this and abort/return early without reading from errc and know that the goroutine and channel will be cleaned up when the function eventually returns.
The buffer size of the errc channel is important here.

Answered By – Dave C

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.