在 Go 语言基础 - channels ( 通道 ) ( 一 ) 中我们讲解了 Go 语言 channel
的一些基本使用。本章节我们就来探索一些高级的知识
channel 类型
将一个 channel
类型作为一个函数的参数或返回值时,channel
操作符采用的则是另一种表现,也就是
channel
类型仅仅定义了单向信道,即 「 仅接收 」 和 「 仅发送 」 channel
仅接收 channel
下面的代码声明了一个仅用于接收值的 channel
arg <- chan int
仅发送 channel
下面的代码则声明了一个仅用于发送值的 channel
arg chan <- int
这个语法有点另类,也有点难记,因为把
chan
和int
拆开了
范例
package main func getRcvOnlyChan() <- chan int { return make(chan int) } func main() { c := getRcvOnlyChan() c <- 1 }
运行结果如下
$ go run demo.go # command-line-arguments ./demo.go:9:4: invalid operation: c <- 1 (send to receive-only type <-chan int)
陷阱
- 单向
channel
无法转换回双向channel
特征
「 不缓冲 」 和 「 零缓冲 」 channel
「 不缓冲 」 和 「 零缓冲 」 channel
在仅在发送方和接收方之间同步发送一个类型的值,换句话说,发送值会阻止发送方的执行,直到接收方准备好读取该值,同样的,接收值会阻止接收方的执行,直到发送者准备发送值为止。值的传输方式: 值直接复制到接收器的堆栈
「 可缓冲 」 channel
一个 「 可缓冲 」 channel
会在发送器和接收器之间传输一个类型的值,采用一下两种方式之一
- 异步,如果缓冲区有剩余的可用空间
- 同步,如果缓冲区已满了,此时会发生阻塞
「 空值 」 channel
往 「 空值 」 channel
发送值或接收值会无限期的阻塞操作
这种行为看起来有用 ?
是的。
一般用于 select
语句中。
一般情况下,当需要等待可用的 channel
的情况下,可以使用一个 「 已关闭 」channel
来无限期的阻塞,直到可用 channel
可用为止。当然了,它也有副作用,就是浪费了不少时间从 「 已关闭 」channel
来读取 零值 ,但如果使用 「 空值 」channel
,从 「 空值 」channel
通道转换到 可用 channel
时则不会发生这种情况
select
channel
正如我们所发现的那样,由于 channel
的性质或者队列已满,channel
操作可能会阻塞。那么我们要如何编写在一对多 channel
上运行的 goroutine
,而不会一直看到特定 channel
停止执行
这种时候,select
语句就派上用场了,此语句将阻塞,直到其分支中的至少一个可以执行,如果多个分支有资格执行,则以非确定性方式随机挑选一个分支
要终止从其某个分支执行语句,请使用 break
语句
范例
package main import "fmt" func main() { a := make(chan int) b := make(chan int) go func() { select { case val := <-a: fmt.Println("Received from a:", val) case val := <-b: fmt.Println("Received from b:", val) } }() b <- 520 }
运行结果如下
$ go run demo.go
Received from b: 0
出现输出 Received from b: 0
是概率事件,因为这种情况下,其实两个通道都可用,所以会随机选择一个
最佳实战
因为在循环中包含 channel
select
是很常见的,请记住使用 break
终止执行最里面的 for
,select
或 switch
语句