SoFunction
Updated on 2025-03-01

Example of how Golang elegantly closes the channel

Preface

Recently, I used Go to develop back-end services. The service shutdown requires ensuring that the data in the channel has been read. The reason is very simple. After receiving the system interrupt signal, the system needs to finish the work to ensure that the data in the channel has to be processed before the system can be shut down. But it is not that simple to implement. Let’s take a look at the detailed introduction.

Criticisms about Go channel design and specification:

  • There is no simple and common way to check if the channel is closed if it cannot be changed.
  • Close the closed channel will cause panic, so it is dangerous to close the channel when the closer does not know whether the channel has been closed.
  • Sending a value to a closed channel will result in panic, so it is dangerous if the sender (sender) sends a value to the channel without knowing whether the channel has been closed.

Therefore, the built-in close method of Golang can close the channel. If you send data to the closed channel, an error will be reported:panic: close of closed channel.

Look at the following code. For a period of time, the producer can continuously write data to the channel and the consumer processes it. After a period of time, the channel is closed. If there is still data sent to the channel, the program will report an error.

package main
 
import (
 "fmt"
 "sync"
 "time"
)
 
func main() {
 jobs := make(chan int)
 var wg 
 go func() {
 ( * 3)
 close(jobs)
 }()
 go func() {
 for i := 0; ; i++ {
 jobs <- i
 ("produce:", i)
 }
 }()
 (1)
 go func() {
 defer ()
 for i := range jobs {
 ("consume:", i)
 }
 }()
 ()
}

The probability of errors occurring after several more runs is relatively high:

produce: 33334
consume: 33334
consume: 33335
produce: 33335
produce: 33336
consume: 33336
consume: 33337
produce: 33337
produce: 33338
consume: 33338
consume: 33339
produce: 33339
produce: 33340
consume: 33340
panic: send on closed channel
 
goroutine 19 [running]:
panic(0x49b660, 0xc042410bb0)
  C:/Go/src/runtime/:500 +0x1af
.func2(0xc04203a180)
  C:/Users/tanteng/Go/src/examples/channel_close.go:18 +0x6b
created by 
  C:/Users/tanteng/Go/src/examples/channel_close.go:21 +0xb8
exit status 2

How to close the channel gracefully

So how do you determine whether the channel is closed before sending data to the channel?

1._,ok := <- jobs

At this time, if the channel is closed, the ok value is false. If the channel is not closed, a job will be missed.

2. Use the select method

Create another channel, called timeout. If the timeout is sent to this channel, send data to the channel of the jobs at the producer, listen to timeout with select, and close the channel of the jobs if the timeout is exceeded.

The complete code is as follows:

package main
 
import (
 "fmt"
 "sync"
 "time"
)
 
func main() {
 jobs := make(chan int)
 timeout := make(chan bool)
 var wg 
 go func() {
 ( * 3)
 timeout <- true
 }()
 go func() {
 for i := 0; ; i++ {
 select {
 case <-timeout:
 close(jobs)
 return
 
 default:
 jobs <- i
 ("produce:", i)
 }
 }
 }()
 (1)
 go func() {
 defer ()
 for i := range jobs {
 ("consume:", i)
 }
 }()
 ()
}

This ensures that data will not be sent to the closed channel.

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.