## Channel 那么如果启动了多个 goroutine,它们之间该如何通信呢?这就是 Go 语言提供的 channel(通道)要解决的问题。 **创建一个 channel** ``` ch := make(chan 数据类型) ch1 := make(chan int) // 创建一个整型类型的通道 ch2 := make(chan interface{}) // 创建一个空接口类型的通道, 可以存放任意格式 type Equip struct{ /* 一些字段 */ } ch2 := make(chan *Equip) // 创建Equip指针类型的通道, 可以存放*Equip ``` **发送数据** 通道的发送使用特殊的操作符 `<-`,将数据通过通道发送的格式为: 通道变量 <- 值 使用 `make` 创建一个通道后,就可以使用`<-`向通道发送数据,代码如下: ``` // 创建一个空接口通道 ch := make(chan interface{}) // 将0放入通道中 ch <- 0 // 将hello字符串放入通道中 ch <- "hello" ``` 把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。 **接收数据** 通道接收同样使用`<-`操作符 通道的数据接收一共有以下写法。 1. 阻塞接收数据 阻塞模式接收数据时,将接收变量作为`<-`操作符的左值,格式如下: ``` data := <-ch ``` 执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。 2. 忽略接收的数据 阻塞接收数据后,忽略从通道返回的数据,格式如下: ``` <-ch ``` 执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。 3. 循环接收 通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下: ``` for data := range ch { } ``` 通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。 ``` package main import ( "fmt" "time" ) func main() { // 构建一个通道 ch := make(chan int) // 开启一个并发匿名函数 go func() { // 从3循环到0 for i := 3; i >= 0; i-- { // 发送3到0之间的数值 ch <- i // 每次发送完时等待 time.Sleep(time.Second) } }() // 遍历接收通道数据 for data := range ch { // 打印通道数据 fmt.Println(data) // 当遇到数据0时, 退出接收循环 if data == 0 { break } } } ``` ### 无缓冲 channel 上面的示例中,使用 make 创建的 chan 就是一个无缓冲 channel,它的容量是 0,不能存储任何数据。所以无缓冲 channel 只起到传输数据的作用,数据并不会在 channel 中做任何停留。这也意味着,无缓冲 channel 的发送和接收操作是同时进行的,它也可以称为同步 channel。 ### 有缓冲 channel 有缓冲 channel 类似一个可阻塞的队列,内部的元素先进先出。通过 make 函数的第二个参数可以指定 channel 容量的大小,进而创建一个有缓冲 channel,如下面的代码所示: ``` cacheCh:=make(chan int,5) ``` 一个有缓冲 channel 具备以下特点: 1. 有缓冲 channel 的内部有一个缓冲队列; 2. 发送操作是向队列的尾部插入元素,如果队列已满,则阻塞等待,直到另一个 goroutine 执行,接收操作释放队列的空间; 3. 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,直到另一个 goroutine 执行,发送操作插入新的元素。 因为有缓冲 channel 类似一个队列,可以获取它的容量和里面元素的个数。如下面的代码所示: ``` cacheCh:=make(chan int,5) cacheCh <- 2 cacheCh <- 3 fmt.Println("cacheCh容量为:",cap(cacheCh),",元素个数为:",len(cacheCh)) ``` 通过内置函数 cap 可以获取 channel 的容量,也就是最大能存放多少个元素,通过内置函数 len 可以获取 channel 中元素的个数。 ### 单向 channel 有时候,我们有一些特殊的业务需求,比如限制一个 channel 只可以接收但是不能发送,或者限制一个 channel 只能发送但不能接收,这种 channel 称为单向 channel。 单向 channel 的声明也很简单,只需要在声明的时候带上 <- 操作符即可,如下面的代码所示: ``` // 接收通道 ch := make(chan<- int) // 发送通道 ch := make(<-chan int) ``` ### 关闭 channel channel 还可以使用内置函数 `close`关闭。关闭channel之后就无法再发送任何数据了,在消费方可以通过语法`v, ok := <-ch`获取channel是否被关闭。如果ok返回false,那么说明channel已经没有任何数据并且已经被关闭。