066-单向 channel
原创
©著作权归作者所有:来自51CTO博客作者mb63083a7dd962a的原创作品,请联系作者获取转载授权,否则将追究法律责任
在 Golang 中,channel 也是一种引用数据类型。还记得我们学过哪些引用类型吗?
所以 channel 作为参数,函数内对 channel 的修改,会直接影响到函数外面的变量。现在我们把上一节的 pipeline 进行改写,封装成函数的形式。
1. 封装 counter 和 squarer
package main
import "fmt"
// out 参数是传出参数
func counter(out chan int) {
for x := 0; x < 100; x++ {
out <- x
}
close(out)
}
// out 参数是传出参数,in 是传入参数
func squarer(out, in chan int) {
for x := range in {
out <- x * x
}
close(out)
}
func main() {
naturals := make(chan int)
squares := make(chan int)
// 启动两个协程
go counter(naturals)
go squarer(squares, naturals)
for x := range
naturals 和 squares 把三个协程串联起来,使得代码更加容易理解。上面的代码没有任何问题,但是……接下来才是本文的重点。
2. 单向 channel
正如上一节所描述的,有些 channel 是用于传入数据,而有些是用于传出的。单纯的依靠参数名 out 和 in 来做区分并不十分严格。为什么这么说呢?比如函数:
func counter(out chan int)
尽管 counter 函数的参数告诉我们 out 是传出参数,但是作为实现者,谁参保证他一定就不会从 out 读取数据呢?such as:
func counter(out chan int) {
var y int
for x := 0; x < 100; x++ {
out <- x
y = <- out // 你几乎无法阻止有些程序员做这样的事情,尤其是函数逻辑复杂的情况下。
out <- x
}
close(out)
}
万一真的出现这种情况,导致程序出现 bug,那就非常难查……因此,我们希望 Golang 编译器帮我做这样的限制—— 有些类型的 channel 只能读,而有些只能写。
这样一来,大多数这种『意外』的错误就能被编译器检查出来,减少犯错的机会。那如何声明这种单向 channel,非常简单:
// 下面的写法没什么意义,但是为了说明问题,只是作为一个示例
out := make(<-chan int) // 创建一个只能读的 channel
in := make(chan<- int) // 创建一个只能写的 channel
通常创建一个单向的 channel 并没什么意义,但是在 Golang ,允许将双向通道赋值给单向通道:
var reader <-chan int // 声明只读单向通道
naturals := make(chan int) // 创建双向通道
reader = naturals // 将双向通道赋值给单向通道
naturals <- 100 // OK!
reader <- 100 // NOT OK!
x := <- reader // OK!
有同学可能记不住单向通道的箭头怎么写,到底是
-
->chan int
-
chan-> int
-
<-chan int
-
chan<- int
是不是彻底凌乱了?其实很容易记忆,所有向右的箭头都是错误的。那么就剩下 <-chan int
和 chan<- int
的区别了。这更简单,chan
是通道,那 <-chan int
就表示数据从通道流出,而 chan<- int
则表示数据流入通道。
还有一点提示的是,在 Golang 中,<-
和 chan
关键字总是连起来写,中间不要有空格。
好了,我们继续把第 1 节的程序改写一下:
package main
import "fmt"
// 对于 counter 来说,只能往 out 里写数据
func counter(out chan<- int) {
for x := 0; x < 100; x++ {
out <- x
}
close(out)
}
// 对于 squarer 来说,只能往 out 里写数据,只能从 in 读数据
func squarer(out chan<- int, in <-chan int) {
for x := range in {
out <- x * x
}
close(out)
}
func main() {
naturals := make(chan int)
squares := make(chan int)
go counter(naturals)
go squarer(squares, naturals)
for x := range
3. 总结