单方向的Channel

随着程序的增长,人们习惯于将大的函数拆分为小的函数。我们前面的例子中使用了三个goroutine,然后用两个channels来连接它们,它们都是main函数的局部变量。将三个goroutine拆分为以下三个函数是自然的想法:

func counter(out chan int)
func squarer(out, in chan int)
func printer(in chan int)

其中计算平方的squarer函数在两个串联Channels的中间,因此拥有两个channel类型的参数,一个用于输入一个用于输出。两个channel都拥有相同的类型,但是它们的使用方式相反:一个只用于接收,另一个只用于发送。参数的名字in和out已经明确表示了这个意图,但是并无法保证squarer函数向一个in参数对应的channel发送数据或者从一个out参数对应的channel接收数据。

这种场景是典型的。当一个channel作为一个函数参数时,它一般总是被专门用于只发送或者只接收

为了表明这种意图并防止被滥用,Go语言的类型系统提供了单方向的channel类型,分别用于只发送或只接收的channel。类型chan<- int表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int表示一个只接收int的channel,只能接收不能发送。(箭头<-和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测

记忆技巧:

<- 比喻数据chan 比喻管道int就是管道的传输数据类型.

chan<- 表示数据进入管道,要把数据写进管道,对于调用者就是输出。

<-chan 表示数据从管道出来,对于调用者就是得到管道的数据,当然就是输入。

因为关闭操作只用于断言不再向channel发送新的数据,所以只有在发送者所在的goroutine才会调用close函数,因此对一个只接收的channel调用close将是一个编译错误。

再次修改之前的案例,代码如下:

package main

/*

单方向channel

*/

import (
        "fmt"
        "time"
)

//Counter ---naturals---> Squarer ---squares---> Printer

func Counter(out chan<- int) {
        for x := 0; x < 5; x++ {
                out <- x
                time.Sleep(time.Second)
        }
        //只能关闭输出channel,语法限定
        close(out)
}

func Squarer(out chan<- int, in <-chan int) {
        for x := range in {
                out <- x * x
        }
        //只能关闭输出类型channel,语法限定,
        //因为只有调用输出channel的groutine才能决定数据是否传送完毕
        close(out)
}

func Printer(in <-chan int) {

        for value := range in {
                fmt.Println(value)
        }
}

func main() {
        naturals := make(chan int)
        squares := make(chan int)

        //Counter
        go Counter(naturals)

        //Squarer
        go Squarer(squares, naturals)

        //Printer 执行printer不能用go, 因为那样main groutine会直接退出,导致程序退出
        Printer(squares)

        fmt.Println("done!")
}

调用counter(naturals)时,naturals的类型将隐式地从chan int转换成chan<- int。调用printer(squares)也会导致相似的隐式转换,这一次是转换为<-chan int

类型只接收型的channel。任何双向channel向单向channel变量的赋值操作都将导致该隐式转换。这里并没有反向转换的语法:也就是不能将一个类似chan<- int类型的单向型的channel转换为chan int类型的双向型的channel。

results matching ""

    No results matching ""