不要从接收端关闭通道,如果通道有多个并发发送方,也不要关闭通道。 换而言之 ,不要关闭(或向关闭的通道发送值)。否则程序会panic
处理方式:
场景:
- 一个 sender,一个 receiver
- 一个 sender, M 个 receiver
- N 个 sender,一个 reciver
- N 个 sender, M 个 receiver
第 3 种情形下,优雅关闭 channel 的方法是:the only receiver says “please stop sending more” by closing an additional signal channel。
需要说明的是,下面的代码并没有明确关闭 dataCh。在 Go 语言中,对于一个 channel,如果最终没有任何 goroutine 引用它,不管 channel 有没有被关闭,最终都会被 gc 回收。所以,在这种情形下,所谓的优雅地关闭 channel 就是不关闭 channel,让 gc 代劳。
func main() { rand.Seed(time.Now().UnixNano()) const Max = 100000 const NumSenders = 1000 dataCh := make(chan int, 100) stopCh := make(chan struct{}) // senders for i := 0; i < NumSenders; i++ { go func() { for { select { case <- stopCh: return case dataCh <- rand.Intn(Max): } } }() } // the receiver go func() { for value := range dataCh { if value == Max-1 { fmt.Println("send stop signal to senders.") close(stopCh) return } fmt.Println(value) } }() select { case <- time.After(time.Hour): } }
4.
func main() { rand.Seed(time.Now().UnixNano()) const Max = 100000 const NumReceivers = 10 const NumSenders = 1000 dataCh := make(chan int, 100) stopCh := make(chan struct{}) // It must be a buffered channel. toStop := make(chan string, 1) var stoppedBy string // moderator go func() { stoppedBy = <-toStop close(stopCh) }() // senders for i := 0; i < NumSenders; i++ { go func(id string) { for { value := rand.Intn(Max) if value == 0 { select { case toStop <- "sender#" + id: default: } return } select { case <- stopCh: return case dataCh <- value: } } }(strconv.Itoa(i)) } // receivers for i := 0; i < NumReceivers; i++ { go func(id string) { for { select { case <- stopCh: return case value := <-dataCh: if value == Max-1 { select { case toStop <- "receiver#" + id: default: } return } fmt.Println(value) } } }(strconv.Itoa(i)) } select { case <- time.After(time.Hour): } }