## 題目
對已經關閉的的 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包的內存泄漏