通道是一个类型化、线程安全的队列,它允许不同的 go 例程相互通信。可以使用 make 关键字创建通道。
例如:-
<Channel Name> := make(chan <Channel Type>)
我们可以在 Go 语言的通道中发送和接收数据。在 Go 语言中,这个 <- 运算符称为通道运算符。数据按箭头方向流动。此操作将阻塞,直到另一个 go 例程准备好接收该值。示例:- 如果我们想将数据发送到通道,我们将使用以下代码片段:
<Channel name> <- <Input Value>
如果我们想从通道接收数据,我们将使用以下代码片段:
<Variable> := <- <Channel Name>
这会从通道中读取并删除一个值,并将其保存到变量中。该操作将阻塞,直到通道中有值要读取。
死锁和阻塞
死锁是指一组 go 例程都处于阻塞状态,因此没有一个可以继续执行。这是并发编程中我们需要注意的常见错误。
我们可以使用代码片段阻塞并等待,直到通道上发送了某些内容:
<-<Channel Name>
这将阻塞,直到它从通道中弹出单个项目,然后继续,丢弃该项目。
缓冲通道
可以选择对通道进行缓冲。我们可以提供缓冲区长度作为 make() 的第二个参数来创建缓冲通道:
<Channel Name> := make(chan, <Buffer Length>)
仅当缓冲区已满时,才会在缓冲通道上发送阻塞。仅当缓冲区为空时才阻塞接收。
关闭 Go 中的
通道 发送者可以显式关闭通道:
<Channel Name> := make(chan <Channel Type>)
// do some stuff with the channel
close(<Channel Name>)
现在,我们可以通过在变量后面使用“ok”来检查通道是否关闭,例如:
<Variable>,ok := <-<Channel Name>
接收者可以在从通道接收时检查 ok 值,以测试通道是否关闭。
如果 ok 为 false,则通道为空且已关闭。
笔记
_在关闭的通道上发送会导致恐慌。主 go 例程中的恐慌将导致整个程序崩溃,任何其他 go 例程中的恐慌将导致该 go 例程崩溃。
关闭是没有必要的。保持通道打开并没有什么问题,如果不使用它们仍然会被垃圾收集。您应该关闭通道以明确向接收者表明不会遇到任何其他情况。_
通道范围
频道可以范围广泛。
for <item> := range <Channel Name> {
// item is the next value received from the channel
}
此示例将通过通道接收值(如果没有新内容,则在每次迭代时阻塞),并且仅在通道关闭时退出。
选择语句
现在,如果我们有一个 go 例程监听多个通道,并希望按照数据通过每个通道的顺序处理数据。
select 语句用于同时监听多个通道。它类似于 switch 语句,但用于通道。
select {
case i, ok := <- <Channel Name>:
fmt.Println(i)
case s, ok := <- <Channel Name>:
fmt.Println(s)
}
第一个准备好接收值的通道将触发,并且其主体将执行。如果多个通道同时准备好,则随机选择一个。上例中的 ok 变量指的是通道是否已被发送者关闭。
默认情况
如果没有其他通道准备好值,则 select 语句中的默认情况会立即执行。默认情况下会阻止 select 语句阻塞。
select {
case v := <- <Channel Name>:
// use v
default:
// receiving from channel would block
// so do something else
}
有两种类型的通道:只读通道和只写通道。
只读通道 - 通过将通道从 chan 转换为 <-chan 类型,通道可以是只读的。例如:
func main(){
<Channel Name> := make(chan <Type>)
readCh(<Channel Name>)
}
func readCh(<Channel Name> <-chan <Type>) {
// ch can only be read from
// in this function
}
只写通道 - 可以通过更改箭头的位置来创建只写通道。
func writeCh(<Channel Name> chan<-<Type>) {
// ch can only be written to
// in this function
}