## 题目
对已经关闭的的 chan 进行读写,会怎么样?为什么?
## 回答
* 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。
* 如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。
* 如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接成功,返回 channel 元素的零值,但是第二个 bool 值一直为 false。
* 写已经关闭的 chan 会 panic
## 示例
### 1\. 写已经关闭的 chan
~~~go
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,待会会提到。
### 2\. 读已经关闭的 chan
~~~go
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)
}
~~~
输出结果
~~~shell
以下是数值的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
~~~
## 多问一句
### 1\. 为什么写已经关闭的`chan`就会`panic`呢?
~~~go
//在 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"`。
### 2\. 为什么读已关闭的 chan 会一直能读到值?
~~~go
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 会返回对应类型的零值
- Golnag常见面试题目解析
- 交替打印数组和字母
- 判断字符串中字符是否全都不同
- 翻转字符串
- 判断两个给定的字符串排序后是否一致
- 字符串替换问题
- 机器人坐标计算
- 语法题目一
- 语法题目二
- goroutine和channel使用一
- 实现阻塞读的并发安全Map
- 定时与 panic 恢复
- 高并发下的锁与map读写问题
- 为 sync.WaitGroup 中Wait函数支持 WaitTimeout 功能.
- 七道语法找错题目
- golang 并发题目测试
- 记一道字节跳动的算法面试题
- 多协程查询切片问题
- 对已经关闭的的chan进行读写,会怎么样?为什么?
- 简单聊聊内存逃逸?
- 字符串转成byte数组,会发生内存拷贝吗?
- http包的内存泄漏