姊妹篇:
channel需要注意的点:
关闭一个nil channel 会导致程序panic;读写一个nil channel,会造成程序阻塞 Go基础系列:nil channel用法示例
channel关闭之后,仍然可以从channel中读取剩余的数据,直到数据全部读取完成。读取完后继续读,得到的将是对应类型的零值.
而如果继续向已关闭的channel发送数据,会引起panic
channel不能关闭两次,否则会panic
nil channel与closed channel的读写
channel是goroutine之间进行通信的桥梁;
借助于 channel, 不同的 goroutine 之间可以相互通信。
channel 是有类型的,而且有方向,可以把 channel 类比成 unix 中的 pipe。
Go 通过 <- 操作符来实现 channel 的写和读,向管道 中send value时 <- 在 channel 右侧,管道receive value时 <- 在左侧,receive value 不赋值给任何变量是合法的。
Channel 最重要的作用就是传递消息。
关闭一个为nil的channel
关闭一个nil channel 会导致程序panic
所谓nil channel,即如var c chan int这样声明的channel
(与 c := make(chan int, 0)相对应)
1 |
|
执行结果:
1 | <nil> |
而
1 | package main |
执行结果为:
1 | 0xc000064060 |
不会报错
而对于 ch := make(chan int, 0)这种方式创建的channel,在close时不会panic; 但如果定义后便读取而无写入,则会死锁fatal error: all goroutines are asleep - deadlock!; 对于无缓存的channel,只有写入操作也会发生死锁; 对于有缓存的channel,在写入元素数量未达到缓存数时,不会发生报错,代码如下:
1 | package main |
输出:
1 | 0xc000096000 |
读/写一个已经关闭的channel
有两种读取channel的方式,range和<-ch
channel关闭之后,仍然可以从channel中读取剩余的数据,直到数据全部读取完成。
读取完后继续读,得到的将是对应类型的零值.
而 如果继续向已关闭的channel发送数据,会引起panic
读写nil channel 会永久阻塞从而报fatal error;
1 |
|
执行结果:
1 | <nil> |
1 |
|
执行结果:
1 | <nil> |
1 | package main |
输出为:
1 | value: 1 |
1 | package main |
输出为:
1 | 1 |
判断channel是否关闭的方式有两种:
value, ok := <- ch,如果ok为false,即说明此ch已关闭for value := range ch {},如果channel被外部关闭,for循环会退出,即range可以识别出channel被关闭
但对一个已关闭的channel,如果继续向channel发送数据,会引起panic。
channel不能关闭两次,否则会panic
1 | package main |
结果:
1 | panic: close of closed channel |
使用
1 | package main |
在 goroutine 中, 在代码 #1 处向 channel 发送了数据 666 ,在 main 中 #2 处等待数据的接收,如果 c 中没有数据,代码的执行将发生阻塞,直到有 goroutine 开始往 c 中 send value。
因为有<-c存在,故而在向c写入数据前,程序不会退出;用sync包的WaitGroup可以起到同样的阻塞效果;
这是 channel 最简单的用法之一:同步,这种类型的 channel 容量是 0,称之为 unbuffered channel。
“对 unbuffered channel 执行 读 操作 value := <-ch, 会一直阻塞直到有数据可接收,执行 写 操作 ch <- value, 也会一直阻塞 直到有 goroutine 对 channel 开始执行接收,正因为如此在同一个 goroutine 中使用 unbuffered channel 会造成 deadlock。”
1 | package main |
执行报 fatal error: all goroutines are asleep - deadlock! ,读和写相互等待对方从而导致死锁发生。
关于带缓存的通道,IO阻塞问题,参见此文后半部分
1 | package main |
“问题在哪里?问题其实是在 fmt.Println ,一次输出就导致 goroutine 的执行发生了切换(相当于发生了 IO 阻塞),因而即使 c 没有发生阻塞 goroutine 也会让出执行”
即在协程里c<-i的下一行,这个fmt.Println()会发生IO阻塞,当第一次循环,即i=0时,将0写入了c,此时虽然因为未达到缓存的容量3,不会发生阻塞,但fmt.Println()会发生IO阻塞,会向下继续执行
“Channel 可以被close关闭 ,channel 关闭之后仍然可以读取,但是向被关闭的 channel send 会 panic。如果 channel 关闭之前有值写入,关闭之后将依次读取 channel 中的消息,读完完毕之后再次读取将会返回 channel 的类型的 zero value”
无缓存的channel只有在receiver准备好后send才被执行。如果有缓存,并且缓存未满,则send会被执行。
原文链接: https://dashen.tech/2017/12/04/golang之channel入门/
版权声明: 转载请注明出处.