Comment on page

22 数据通道(channel)和多路选择(select)

通道是给定数据类型的值的线程安全的队列。
通道的最重要应用就是协程之间的通信。
因此,我们说把值发送到通道(ch <- value)和从通道中接收值(value <- ch)。
通道的基本用法:
package main
import (
"fmt"
"math/rand"
)
func genInts(chInts chan int) {
chInts <- rand.Intn(1000)
}
func main() {
chInts := make(chan int)
for i := 0; i < 2; i++ {
go genInts(chInts)
}
n1 := <-chInts
fmt.Printf("n1: %d\n", n1)
select {
case n2 := <-chInts:
fmt.Printf("n2: %d\n", n2)
}
}
-----------Output-----------
n1: 81
n2: 887
通道的零值nil。向nil通道中发送数据将会永远阻塞(译者注:程序会异常退出),所以第一件事情就是用make(chan <type>, <queue size>)创建通道。队列大小是可选参数,缺省时创建的就是无缓冲通道(unbuffered channel),你可以认为队列大小为0。
译者注
注意chInts := make(chan int)chInts := new(chan int)的区别。除了前者返回变量值后者返回变量指针之外,前者返回的是队列大小为0的无缓冲通道,后者指向的是零值nil
发送操作符chan <- value会把值放到队列的尾部。
如果队列满了,则发送操作<-会阻塞。
若向nil通道发送,则会永远阻塞。
接收语句value = <- chan则是从队列头部获取值。
如果队列空了,则接收操作会阻塞。
另一个从通道中获取数据的方法是使用select语句。
使用select则允许:
  • 在多个通道上等待
  • 实现非阻塞等待
  • 通过等待一个计时器通道来实现超时
还有另一种获取方法是使用range语句。
通道具有固定大小的容量。
创建通道时若不显式指定队列大小(例如make(chan int)),则队列大小为0,又称为无缓冲通道。向无缓冲通道发送数据会一直阻塞,直到相应的接收操作完成。
创建通道时若显式指定了队列大小(例如make(chan int, 3)),则被称为容量为3的有缓冲通道。前3次发送数据会立即完成,而第4次则会一直阻塞,直到队列中有值被拿走腾出空间。
您可以使用close(chan)语句关闭通道
已关闭的通道再次关闭则会发生恐慌
向已关闭的通道发送数据也会发生恐慌
从已关闭的通道接收数据则会:
  • 返回已缓冲的数据
  • 若已缓冲数据已被取完,则会立即返回零值
关闭通道同样会使正在迭代访问通道的range操作终止。