> For the complete documentation index, see [llms.txt](https://denglj.gitbook.io/essential-go/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://denglj.gitbook.io/essential-go/22-channels-and-select.md).

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

通道是给定数据类型的值的线程安全的队列。

通道的最重要应用就是协程之间的通信。

因此，我们说把值发送到通道(`ch <- value`)和从通道中接收值(`value <- ch`)。

通道的基本用法：

```go
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
```

通道的[零值](/essential-go/02-basic-types/ling-zhi.md)是`nil`。向`nil`通道中发送数据将会永远阻塞(译者注：程序会异常退出)，所以第一件事情就是用`make(chan <type>, <queue size>)`创建通道。队列大小是可选参数，缺省时创建的就是无缓冲通道(unbuffered channel)，你可以认为队列大小为0。

{% hint style="warning" %}
**译者注**

注意`chInts := make(chan int)` 和 `chInts := new(chan int)`的区别。除了前者返回变量值后者返回变量指针之外，前者返回的是队列大小为0的无缓冲通道，后者指向的是零值`nil`。
{% endhint %}

发送操作符`chan <- value`会把值放到队列的尾部。

如果队列满了，则发送操作`<-`会阻塞。

若向`nil`通道发送，则会永远阻塞。

接收语句`value = <- chan`则是从队列头部获取值。

如果队列空了，则接收操作会阻塞。

另一个从通道中获取数据的方法是使用`select`语句。

使用`select`则允许：

* 在多个通道上等待
* 实现非阻塞等待
* 通过等待一个计时器通道来实现超时

还有另一种获取方法是使用[`range`](/essential-go/22-channels-and-select/using-range-to-read-from-a-channel.md)语句。

通道具有固定大小的容量。

创建通道时若不显式指定队列大小(例如`make(chan int)`)，则队列大小为0，又称为无缓冲通道。向无缓冲通道发送数据会一直阻塞，直到相应的接收操作完成。

创建通道时若显式指定了队列大小(例如`make(chan int, 3)`)，则被称为容量为3的有缓冲通道。前3次发送数据会立即完成，而第4次则会一直阻塞，直到队列中有值被拿走腾出空间。

您可以使用`close(chan)`语句[关闭通道](/essential-go/22-channels-and-select/closing-channels.md)。

已关闭的通道再次关闭则会发生[恐慌](/essential-go/20-panic-and-recover/panic.md)。

向已关闭的通道发送数据也会发生[恐慌](/essential-go/20-panic-and-recover/panic.md)。

从已关闭的通道接收数据则会：

* 返回已缓冲的数据
* 若已缓冲数据已被取完，则会立即返回零值

关闭通道同样会使正在迭代访问通道的`range`操作终止。
