看了这几天的文章,感觉要每天一个大类了...这也是醉了,今天我们就来看看 Go 语言中的 channel
对了,百度了一下,发现 channel 没有一个很好的中文名字,虽然我也在 「 管道 」 和 「 通道 」 两个间徘徊,但这里,我还是翻译成 「 通道 」 吧
channel 是一个循环队列,可以同步或异步地传送某种类型的值,一个 channel 可以接收来自多个发送器的值,也可以将值发送给多个接收器

术语
channel 用到了许多有趣的概念,我们将这些概念罗列在此
- 「 Buffered 」( 可缓冲 ) - 内部缓冲区大小大于
1的channel - 「 Empty-buffered 」( 零缓冲 ) - 内部缓冲区大小为
0的channel - 「 Nil 」( 空值 ) -
channel显式 ( 赋值 ) 或隐式 ( 未初始化的变量 ) 设置为nil值 - 「 Unbuffered 」( 不缓冲 ) - 不含内部缓冲区的
channel
生命周期
一个 channel 是通过调用内建函数 make() 来初始化的。此调用会根据正在创建的通道类型接收一个或两个参数。第一个参数是必须的,它表示要创建的 channel 类型,第二个参数是可选的,表示要创建的 channel 缓冲区的大小。第二个通道仅在你需要创建 「 可缓冲 」 的 channel 时才需要
一个 channel 是通过调用内建函数 close() 来关闭的。关闭 channel 的动作是可选的。但它却是发送者告诉接收者该 channel 不再接收任何值了而已 ( 不再接收任何值,也就是不再会发送任何值了 )
声明
声明一个 「 空值 」 的 int 类型的 channel
var c chan int
初始化
初始化一个 「 不缓冲 」 的 channel
c = make( chan int )
初始化一个 「 可缓冲 」的 channel,缓冲区大小为 2
c = make( chan int, 2 )
初始化一个 「 零缓冲 」 的 channel ,缓冲区大小为 0
c = make( chan int, 0 )
关闭或终止 channel
close(c)
陷阱
close(c) 这个动作,并非完美的
-
尝试关闭一个
nil的channel会引发一个异常 ( panic )package main func main() { var c chan int close(c) }运行结果如下
$ go run demo.go panic: close of nil channel goroutine 1 [running]: main.main() /Users/yufei/go/demo.go:5 +0x2a exit status 2
-
尝试关闭一个已经关闭的
channel也会引发一个异常 ( painic )package main func main() { var c chan int c = make( chan int , 0 ) close(c) close(c) }运行结果如下
$ go run demo.go panic: close of closed channel goroutine 1 [running]: main.main() /Users/yufei/go/demo.go:7 +0x57 exit status 2
究其原因,它仅仅是一个通知作用而已,既然缺失了通知的对象,那自然就会报错了
channel 操作
用于与 channel 交互的操作符是 <- 。它用于向 channel 发送值或从中接收值,操作类型取决于 channel 变量的位置
接收操作
从一个 channel 中接收一个值时,变量在操作符的 右边
下面的代码尝试从 channel 变量 c 中接收一个值,并保存在 value 中
value := <- c
下面的代码尝试从 channel 变量 c 中接收一个值,并获取该 channel 是否关闭
value, ok := <- c
当变量 ok 的值为 true 时则表示已关闭,如果为 false 则表示未关闭
下面的代码尝试从 channel 变量 c 中循环的接收值,直到 c 被关闭
for value := range c { // 一些其它逻辑 }
发送操作
当要往一个 channel 中发送值时,变量在操作符的左边,要发送的值在操作符的右边
c <- value
陷阱
- 从一个已经关闭的通道里获取值,永远不会发生阻塞
- 从一个已经关闭的通道获取的值,该值永远是通道类型的空值,比如,如果是
chan int,则值为0,如果是chan string,则值为""
最佳实战
如果你无法理解 <- 到底是用来接收还是用来发送,那么只要把它当作一个箭头就好了,这样就容易理解了
例如
<- c是用于发送的,看这个操作,是不是很像让c往箭头方向看c <-是用于接收的,看这个操作,是不是很像箭头指向c