什么是Channel
Channels可以被认为是Goroutine进行通信的管道。
类似于水在管道中从一端流向另一端的方式,数据可以从一端发送并使用Channel从另一端接收
声明Channel
每个Channels都有一个与之关联的类型
此类型是允许Channel传输的数据类型。不允许使用该Channel运输其他类型
chan T T是Channel类型
Channel的零值为nil。nil在Channels没有任何用处,因此必须使用make类似于maps和slices来定义Channel
Channels发送和接收
data := <- a //read from channel a
a <- data //write to channel a
注意:
默认情况下,Channel的发送和接收是阻塞的
当数据被发送到一个 Channel 时,控制在发送语句中被阻塞,直到某个其他 Goroutine 从该 Channel 读取。同样,当从 Channel 读取数据时,读取会被阻塞,直到某个 Goroutine 将数据写入该 Channel。
Channel 的这种属性有助于 Goroutine 有效地进行通信,而无需使用在其他编程语言中很常见的显式锁或条件变量。
<-done 这一行从信道 done 中读取数据,但是没有使用该数据,也没有将它赋值给其他变量,这是完全合法的。
现在我们的 main 协程被阻塞,等待从信道 done 中读取数据。hello 协程接受信道 done 作为参数,打印 Hello world goroutine 然后将数据写入信道 done 中。当写入完毕后,main 协程从信道 done 中接收到数据,main 协程解除阻塞,继续执行下一条语句,打印:main function。
死锁
如果一个Goroutine在一个Channel上发送数据,那么其他Goroutine应该正在接受数据。
如果这没有发生,程序将在运行时陷入死锁
上面的程序中,创建了一个Channel ch,并通过 ch <- 5 向其中写入 5 。这个程序中没有其他协程从 ch 中接收数据,因此程序在运行时触发 panic,错误如下:
单向Channels
在上面程序中的第 10 行,我们创建了一个只写(send only)信道 sendch 。chan<- int 表示只能发送数据,因为箭头的方向指向 chan。在第 12 行,我们试图从一个只写信道中接收数据,这是非法的,程序将无法通过编译,
如果无法读取它,那么写入仅发送Channels的意义何在!
这就是Channel转换开始使用的地方。可以将双向Channel转换为仅发送or仅接收Channel,反之亦然
在上面程序中的第 10 行,创建了一个双向信道 chnl。在第 11 行它被作为参数传递给协程 sendData。第 5 行,sendData 函数通过形参 sendch chan<- int 将该信道转换成只写信道。因此在 sendData 中该信道为只写信道,而在主协程中该信道为双向信道。程序将打印:10。
关闭通道
发送者可以关闭通道以通知接收者该通道将不再发送数据
接收者在从通道接收数据时可以用一个额外的变量来检查通道是否已经关闭
v,ok := <- ch
上面的语句中 ok 返回 true 表示成功的接收到了发送的数据,如果 ok 返回 false 则表示信道已经被关闭。从已关闭的信道中读取到的数据为信道类型的 0 值。比如从一个被关闭的 int 信道中读取数据,那么将得到 0 。
Buffered Channels
可以使用缓冲区创建通道。
仅当缓冲区已满时,才会阻止发送到缓冲通道。同样,只有当缓冲区为空时,来自缓冲通道的接收才会被阻塞
通过将附加容量参数传递给make指定缓冲区大小的函数来创建缓冲通道
ch := make(chan type , capacity)
上述语法中的capacity应该大于 0,通道才能拥有缓冲区。默认情况下,无缓冲通道的容量为 0
注意:读取的时候也是要读取相应的数据量
select语句
select语句用于从多个send/receive通道操作中进行选择。
select语句会阻塞,直到其中一个发送/接收操作做好准备。如果多个操作准备就绪,则随机选择其中一个。
语法类似于switch
在上面的程序中,第8行中的 server1 函数休眠 6 秒,然后将文本from server1 写入 channel ch, 第12行中的 server2 函数休眠 3 秒,然后将文本from server2 写入 channelch。
main 函数分别在第20行和第21行调用 go Goroutines server1 和 server2。
第22行控制到达 select 语句。select 语句阻塞,直到它的一个用例准备好。在我们上面的程序中,server1 Goroutine 在 6 秒后写入 output1 channel,而 server2 在 3 秒后写入 output2 channel。因此,select 语句将阻塞 3 秒,并等待 server2 Goroutine 写入 output2 channel。3 秒后,程序打印from server2 。
然后将终止。