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操作终止。

最后更新于