源碼分析https://mp.weixin.qq.com/s/eCwZMwGjU2yoXu6K2nGF3g
1 當chan用close關閉時,無論是有緩存的還是無緩存的,返回已緩沖數據或零值,如果重復用close關閉chan或對關閉的chan寫入值都會報錯,
https://blog.csdn.net/qq_41065919/article/details/107729571
https://www.cnblogs.com/pluse/p/12167537.html
struct{}類型好像只有一種具體實例即struct{}{},chan中如果有這種類型,只能寫入它的零值struct{}{},所以類型為struct{}的chan無論是寫入還是讀出都時零值,

func main() { m := make(chan struct{}, 1) // 從空的m中一直讀取值會一直阻塞,所以會報死鎖的錯誤,all goroutines are asleep - deadlock! //b := <- m //fmt.Println(b) // 如果這里就關閉了協程,下面會直接輸出chan中的零值,而不會走default, //close(m) //m <- struct{}{} go func() { for{ select { case a := <- m: fmt.Println(a) time.Sleep(time.Second) default: fmt.Println("chan為空,讀取的時候會阻塞") time.Sleep(time.Second) } } }() // 加這個是為了主協程等待里面的子協程,否則的話主協程直接執行完了,觀察不到子協程的具體輸出, time.Sleep(time.Second * 5) }
a 如果close無緩存channel,
1> close前沒有因為寫入阻塞,則讀出的是channe里類型的零值,
2> 如果寫入阻塞了,則第一次讀出的是寫入的值,之后讀出的是零值,

func main() { m := make(chan int) go func() { m <- 555 // 這里雖然close了,但之后第一次讀出的仍是第一次寫入的值, close(m) // 保證close在讀之前 time.Sleep(time.Second * 3) }() go func() { for{ select { case a := <- m: if a != 0{ fmt.Println("第一次從channel中讀出的值為colse前寫入的值", a) } else{ fmt.Println("之后讀出的值為int類型的零值", a) } time.Sleep(time.Second) default: fmt.Println("chan為空,讀取的時候會阻塞") time.Sleep(time.Second) } } }() time.Sleep(time.Second * 4) }
b 如果close有緩存channel,
1> close前沒有寫入,則讀出的是channel里類型的零值,
2> 如果close前寫入了,則會按隊列的順序把數據依次取出來,

func main() { m := make(chan int, 3) m <- 111 m <- 222 close(m) go func() { for{ select { case a := <- m: if a != 0{ fmt.Println("讀出的值是之前寫入的值", a) } else{ fmt.Println("之后讀出的值為int類型的零值", a) } time.Sleep(time.Second) default: fmt.Println("chan為空,讀取的時候會阻塞") time.Sleep(time.Second) } } }() time.Sleep(time.Second * 4) }