066-单向 channel
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了066-单向 channel相关的知识,希望对你有一定的参考价值。
在 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 squares
fmt.Println(x)
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 squares
fmt.Println(x)
3. 总结
- 掌握单向通道声明
- 知道单向通道存在的意义
以上是关于066-单向 channel的主要内容,如果未能解决你的问题,请参考以下文章
Golang 基础:原生并发 goroutine channel 和 select 常见使用场景