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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://denglj.gitbook.io/essential-go/22-channels-and-select.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
