c := make(chanint, 10) c <- 1 c <- 2 c <- 3 close(c)
for { data, ok := <-c if !ok { return } /* 输出: 1 true 2 true 3 true */ fmt.Println(data, ok) } }
答案:由此可见,有缓存通道在通道被关闭时,还是可以正常读取的
那如果是无缓冲通道会怎样?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
funcmain() { c := make(chanint) c <- 1 c <- 2 c <- 3 close(c) for { data, ok := <-c if !ok { return } /* 输出:fatal error: all goroutines are asleep - deadlock! */ fmt.Println(data, ok) } }
答案:无缓冲通道会发生死锁(deadlock),是因为无缓冲通道在进行写操作时需要有对应的读操作来处理数据。如果没有对通道的读操作,写操作在执行时会阻塞,直到有一个读操作准备好接收数据。因此,当你尝试执行 c <- 1 时,程序会一直等待,此时1并没有被写入到通道 c 中,直到有一个 goroutine 从通道中读取数据。如果没有 goroutine 进行读取,程序将会阻塞在写入操作上,导致死锁并引发 panic。
// 多个生产者 for i := 0; i < NumSenders; i++ { gofunc(id string) { for { value := rand.Intn(MaxRandomNumber) // 命中关闭,通知中间人去干关闭的活 if value == 0 { select { case toStop <- "sender#" + id: default: } return }
//尝试尽早退出, 这里不能省略, 因为可能会导致多发送一次? select { case <-stopCh: return default: }
// 生产数据 select { case <-stopCh: // 监听 stopCh关闭信号,跳出循环 return case dataCh <- value: } } }(strconv.Itoa(i)) }
// 多个消费者 for i := 0; i < NumReceivers; i++ { gofunc(id string) { defer wg.Done()
for { // 尝试尽早退出, 这里不能省略, 因为可能会导致多接收一次? select { case <-stopCh: return default: }