Golang gracefully closes the channel method example

  • 2020-06-12 09:21:55
  • OfStack

preface

In the recent development of backend services using go, service shutdown requires to ensure that all data in channel is read, for the simple reason that after receiving the interrupt signal from the system, the system needs to do some finishing work to ensure that all data in channel is processed before the system can be shut down. But the implementation is not that simple, let's start with 1 to see the details.

Criticisms of the Go channel design and specification:

There is no simple universal way to check that channel is down without changing the state of channel Closing a closed channel results in panic, so it is dangerous to close channel without knowing whether closer(the shut-off) is closed Sending a value to a closed channel results in panic, so it is dangerous if sender(the sender) sends a value to channel without knowing if channel is closed

So Golang's built-in close method can turn off channel. If you send data to channel that has been turned off, an error will be reported: panic: close of closed channel.

As shown in the following code, during a period of time, the producer can continuously write data to channel for the consumer to process. After a period of time, channel shuts down. At this time, if there is still data sent to channel, the program will report an error.


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

Run more than a few times will have a higher probability of error:


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/panic.go:500 +0x1af
main.main.func2(0xc04203a180)
  C:/Users/tanteng/Go/src/examples/channel_close.go:18 +0x6b
created by main.main
  C:/Users/tanteng/Go/src/examples/channel_close.go:21 +0xb8
exit status 2

How do I gracefully close channel

So how do you tell if a channel is closed before sending data to it?

1._,ok := < - jobs

If channel is turned off, the value of ok is false, and if channel is not turned off, one jobs will be missed

2. Use select

Create another channel called timeout, send true to this channel if timeout, send data to channel of jobs in producer, listen to timeout with select, if timeout, close channel of jobs.

The complete code is as follows:


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

This ensures that no data is sent to channel that has been closed.

conclusion


Related articles: