Closing channels

In the previous sections, we have looked at three types of channels and how to create them. In this section, let's look at how to close the channels and how this might affect sending and receiving on these channels. We close a channel when we no longer want to send any messages on the said channel. How a channel behaves after being closed is different for each type of channel. Let's dive into them:

  • Unbuffered closed channel: Sending messages will cause panic and receiving on it will yield an immediate zero value of the channel's element type.
  • Buffered closed channel: Sending messages will cause panic but receiving on it will first yield all the values in the channel's queue. Once the queue has been exhausted, then the channel will start yielding zero values of the channel's element type.

The following is a program to elucidate on the two preceding points:

// closed.go 
package main 
 
import "fmt" 
 
type msg struct { 
    ID    int 
    value string 
} 
 
func handleIntChan(intChan <-chan int, done chan<- int) { 
    // Even though there are only 4 elements being sent via channel, we retrieve 6 values. 
    for i := 0; i < 6; i++ { 
        fmt.Println(<-intChan) 
    } 
    done <- 0 
} 
 
func handleMsgChan(msgChan <-chan msg, done chan<- int) { 
    // We retrieve 6 values of element type struct 'msg'. 
    // Given that there are only 4 values in the buffered channel, 
    // the rest should be zero value of struct 'msg'. 
    for i := 0; i < 6; i++ { 
        fmt.Println(fmt.Sprintf("%#v", <-msgChan)) 
    } 
    done <- 0 
} 
 
func main() { 
    intChan := make(chan int) 
    done := make(chan int) 
 
    go func() { 
        intChan <- 9 
        intChan <- 2 
        intChan <- 3 
        intChan <- 7 
        close(intChan) 
    }() 
    go handleIntChan(intChan, done) 
 
    msgChan := make(chan msg, 5) 
    go func() { 
        for i := 1; i < 5; i++ { 
            msgChan <- msg{ 
                ID:    i, 
                value: fmt.Sprintf("VALUE-%v", i), 
            } 
        } 
        close(msgChan) 
    }() 
    go handleMsgChan(msgChan, done) 
 
    // We wait on the two channel handler goroutines to complete. 
    <-done 
    <-done 
 
    // Since intChan is closed, this will cause a panic to occur. 
    intChan <- 100 
} 

The following is one possible output of the program:

9
2
3
7
0
0
main.msg{ID:1, value:"VALUE-1"}
main.msg{ID:2, value:"VALUE-2"}
main.msg{ID:3, value:"VALUE-3"}
main.msg{ID:4, value:"VALUE-4"}
main.msg{ID:0, value:""}
main.msg{ID:0, value:""}
panic: send on closed channel
    
goroutine 1 [running]:
main.main()
          closed.go:58 +0x194
    
    Process finished with exit code 2
  

Finally, here are some further useful points about closing channels and closed channels:

  • It is not possible to determine if a channel has been closed. The best we can do is check if we were able to successfully retrieve a message from a channel. We know that the default syntax for retrieving on channel is msg := <- ch. However, there is a variant on this retrieval: msg, ok := <-ch. The second parameter tells us if the retrieval was successful. If a channel is closed, ok will be false. This can be used to tell when a channel has been closed.
  • msg, ok := <-ch is a common pattern when iterating over channels. As a result, Go allows us to range over a channel. When a channel closes, the range loop ends.
  • Closing a closed channel, nil channel, or a receive-only channel will cause panic. Only a bidirectional channel or send-only channel can be closed.
  • It is not mandatory to close a channel and irrelevant for the garbage collector (GC). If the GC determines that a channel is unreachable, irrespective of whether it is open or closed, the channel will be garbage collected.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.107.193