Explicitly closing channels

Example below works fine (not sure if it is the best practise though) with/without explicitly closing channels. However, what I want to know is, is there any value/benefit of explicitly closing channels or leaving it to memory manager to handle/close them?

Some resources confusing and make similar remarks such as:

  • Close what you opened after finishing with them.
  • Don’t worry too much about closing channels because Go is good at handling things like that.

I have a strong feeling that, it is better to close them because a book I read reads: “Channels carry overhead and have performance impact… Channnels are single biggest source of memory management issues in Go programs.

Note: I know I could use “timeout” to exit the loop but using done channel on purpose.

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	fmt.Println("main: begin")
    
    	names := []string{"John", "Robert", "Al", "Rick"}
    
    	msg := make(chan string)
    	done := make(chan bool)
    
    	go greet(msg, done, names)
    
    	Loop:
    		for {
    			select {
    			case m := <-msg:
    				fmt.Println(m)
    			case <-done:
    				fmt.Println("main: done")
    				//close(done)
    				break Loop
    			}
    		}
    
    	fmt.Println("main: end")
    }
    
    func greet(msg chan<- string, done chan<- bool, name []string) {
    	for _, name := range name {
    		msg <- fmt.Sprintf("Hi %s!", name)
    	}
    
    	//close(msg)
    
    	done <- true
    }
main: begin
Hi John!
Hi Robert!
Hi Al!
Hi Rick!
main: done
main: end

Note that in your example a single channel msg would be enough. As soon as greet calls close(msg), a for loop readig values m := <-msg would end. The second chanel done and the select are not necessary.

But I guess this is not what you question is about :wink:

1 Like

Your comment is perfectly fine and much appreciated. Learning in progress …

Maybe you also fine this article useful - How to Gracefully Close Channels

1 Like

@GreyShatter I read it before and I think too much going on there. I lost my bearings.

@lutzhorn Do you mind showing me how exactly I should refactor it because when I do what you suggest, it either prints once and exit or don’t exit at all. I bet am missing something.

Strange, usually they articles are very good and simple enough.

As for refactoring:

package main

import (
	"fmt"
)

func main() {
	fmt.Println("main: begin")

	names := []string{"John", "Robert", "Al", "Rick"}

	msg := make(chan string)

	go greet(msg, names)

	for m := range msg {
		fmt.Println(m)
	}

	fmt.Println("main: end")
}

func greet(msg chan<- string, names []string) {
	for _, name := range names {
		msg <- fmt.Sprintf("Hi %s!", name)
	}
	close(msg)
}

https://play.golang.org/p/75vASSZdAbf

1 Like

Excellent. Thanks. I remember seeing range bit before but completely forgot about it. Now it is time for me to wait for comments on “to close or not to close channels” question.

Well it’s totally okay - https://stackoverflow.com/questions/8593645/is-it-ok-to-leave-a-channel-open

But with close GC release memory sooner and your code became more readable so I personally always exlicitly close channels.

Try something like this:

package main

import (
	"fmt"
)

func main() {
	fmt.Println("main: begin")

	names := []string{"John", "Robert", "Al", "Rick"}

	msg := make(chan string)

	go greet(msg, names)

	// A: Using range on the channel will loop until the channel is closed.
	for m := range msg {
		fmt.Println(m)
	}

	fmt.Println("main: end")
}

func greet(msg chan<- string, name []string) {
	for _, name := range name {
		msg <- fmt.Sprintf("Hi %s!", name)
	}

	// Closing msg here will terminate the loop at A.
	close(msg)
}

See https://play.golang.com/p/864dawlHgiD

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.