一、如何判断channel是否已经关闭?
用 select 和 <-ch 来结合可以解决这个问题
ok的结果和含义:
true:读到数据,并且通道没有关闭。
false:通道关闭,无数据读到。
ch := make(chan int, 10)
go func() {
for i:=1;i<10;i++{
ch <- i
}
}()
time.Sleep(1*time.Second)
close(ch)
for i:=0;i<10;i++{
select {
case v, ok := <- ch:
if ok {
fmt.Println(v)
}else{
fmt.Println("关掉了")
}
default:
fmt.Println("没啥事")
}
}
需要注意:
1.case 的代码必须是 _, ok:= <- ch 的形式,如果仅仅是 <- ch 来判断,是错的逻辑,因为主要通过 ok的值来判断;
2.select 必须要有 default 分支,否则会阻塞函数,我们要保证一定能正常返回;
官方推荐通过 context 来配合使用,我们可以通过一个 ctx 变量来指明 close 事件,而不是直接去判断 channel 的一个状态
select {
case <-ctx.Done():
// ... exit
return
case v, ok := <-c:
// do something....
default:
// do default ....
}
二、golang 对已经关闭的的chan进行读写,会怎么样?
1、写已经关闭的channel
package main
func main(){
c:= make(chan int,2)
close(c)
c <- 1
}
运行结果:
会发生panic
2、读已经关闭的channel
package main
import (
"fmt"
)
func main(){
c:= make(chan int,2)
c <- 1
close(c)
num,ok :=<-c
fmt.Println(num,ok)
num,ok =<-c
fmt.Println(num,ok)
num,ok =<-c
fmt.Println(num,ok)
}
运行结果:
- 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。
- 如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。
- 如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接返回,返回 channel 元素的零值(如果是string就返回空字符串),但是第二个 bool 值一直为 false。