对已经关闭的的 chan 进行读写,会怎么样?为什么?
- 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。
- 如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。
- 如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接成功,返回 channel 元素的零值,但是第二个 bool 值一直为 false。
- 写已经关闭的 chan 会 panic
func main(){
c := make(chan int,3)
close(c)
c <- 1
}
//输出结果
panic: send on closed channel
goroutine 1 [running]
main.main()
...
- 注意这个 send on closed channel,待会会提到。
package main
import "fmt"
func main() {
fmt.Println("以下是数值的chan")
ci:=make(chan int,3)
ci<-1
close(ci)
num,ok := <- ci
fmt.Printf("读chan的协程结束,num=%v, ok=%v\n",num,ok)
num1,ok1 := <-ci
fmt.Printf("再读chan的协程结束,num=%v, ok=%v\n",num1,ok1)
num2,ok2 := <-ci
fmt.Printf("再再读chan的协程结束,num=%v, ok=%v\n",num2,ok2)
fmt.Println("以下是字符串chan")
cs := make(chan string,3)
cs <- "aaa"
close(cs)
str,ok := <- cs
fmt.Printf("读chan的协程结束,str=%v, ok=%v\n",str,ok)
str1,ok1 := <-cs
fmt.Printf("再读chan的协程结束,str=%v, ok=%v\n",str1,ok1)
str2,ok2 := <-cs
fmt.Printf("再再读chan的协程结束,str=%v, ok=%v\n",str2,ok2)
fmt.Println("以下是结构体chan")
type MyStruct struct{
Name string
}
cstruct := make(chan MyStruct,3)
cstruct <- MyStruct{Name: "haha"}
close(cstruct)
stru,ok := <- cstruct
fmt.Printf("读chan的协程结束,stru=%v, ok=%v\n",stru,ok)
stru1,ok1 := <-cs
fmt.Printf("再读chan的协程结束,stru=%v, ok=%v\n",stru1,ok1)
stru2,ok2 := <-cs
fmt.Printf("再再读chan的协程结束,stru=%v, ok=%v\n",stru2,ok2)
}
输出结果
以下是数值的chan
读chan的协程结束,num=1, ok=true
再读chan的协程结束,num=0, ok=false
再再读chan的协程结束,num=0, ok=false
以下是字符串chan
读chan的协程结束,str=aaa, ok=true
再读chan的协程结束,str=, ok=false
再再读chan的协程结束,str=, ok=false
以下是结构体chan
读chan的协程结束,stru={haha}, ok=true
再读chan的协程结束,stru=, ok=false
再再读chan的协程结束,stru=, ok=false
//在 src/runtime/chan.go
func chansend(c *hchan,ep unsafe.Pointer,block bool,callerpc uintptr) bool {
//省略其他
if c.closed != 0 {
unlock(&c.lock)
panic(plainError("send on closed channel"))
}
//省略其他
}
- 当
c.closed != 0
则为通道关闭,此时执行写,源码提示直接panic
,输出的内容就是上面提到的"send on closed channel"
。
func chanrecv(c *hchan,ep unsafe.Pointer,block bool) (selected,received bool) {
//省略部分逻辑
lock(&c.lock)
//当chan被关闭了,而且缓存为空时
//ep 是指 val,ok := <-c 里的val地址
if c.closed != 0 && c.qcount == 0 {
if receenabled {
raceacquire(c.raceaddr())
}
unlock(&c.lock)
//如果接受之的地址不空,那接收值将获得一个该值类型的零值
//typedmemclr 会根据类型清理响应的内存
//这就解释了上面代码为什么关闭的chan 会返回对应类型的零值
if ep != null {
typedmemclr(c.elemtype,ep)
}
//返回两个参数 selected,received
// 第二个采纳数就是 val,ok := <- c 里的 ok
//也就解释了为什么读关闭的chan会一直返回false
return true,false
}
}
c.closed != 0 && c.qcount == 0
指通道已经关闭,且缓存为空的情况下(已经读完了之前写到通道里的值)- 如果接收值的地址
ep
不为空- 那接收值将获得是一个该类型的零值
typedmemclr
会根据类型清理相应地址的内存- 这就解释了上面代码为什么关闭的 chan 会返回对应类型的零值