传送channel的通道

传送channel的通道 是一种特殊的通道变量,它与通道工作而不是其他类型的变量。不过,您仍然要给一个传送 channel 的通道声明一个数据类型。您可以在一行使用 chan 关键字两次定义一个传送 channel 的通道,如下所示:

  1. c1 := make(chan chan int)

这章介绍的其他类型的通道都比传递channel的通道要简单方便。

chSquare.go 代码来说明传递channel的通道的使用,分为四个部分来介绍。

chSquare.go 的第一部分如下:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "strconv"
  6. "time"
  7. )
  8. var times int

chSquare.go 的第二段代码如下:

  1. func f1(cc chan chan int, f chan bool) {
  2. c := make(chan int)
  3. cc <- c
  4. defer close(c)
  5. sum := 0
  6. select {
  7. case x := <-c:
  8. for i := 0; i <= x; i++ {
  9. sum = sum + i
  10. }
  11. c <- sum
  12. case <-f:
  13. return
  14. }
  15. }

声明一个常规的 int 通道,您把它发送给传递通道的通道变量。然后您使用 select 表达式从常规的 int 通道读取数据和使用 f 信号通道来退出函数。

一旦您从 c 通道读取了信号值,便开始了一个 for 循环来计算从 0 到信号值的所有整数之和。接下来,您发送这个计算值给 c int 通道,执行结束。

chSquare.go 的第三部分如下:

  1. func main() {
  2. arguments := os.Args
  3. if len(arguments) != 2 {
  4. fmt.Println("Need just one integer argument!")
  5. return
  6. }
  7. times, err := strconv.Atoi(arguments[1])
  8. if err != nil {
  9. fmt.Println(err)
  10. return
  11. }
  12. cc := make(chan chan int)

上面代理里的最后一句表达式声明了一个名为 cc 的传递通道的通道变量,它是这个程序的开始,因为一切都依赖与它:cc 变量传递给 f1() 函数,并被接下来的 for 循环中使用。

chSquare.go 的其他代码如下:

  1. for i := 1; i < times + 1; i++ {
  2. f := make(chan bool)
  3. go f1(cc, f)
  4. ch := <-cc
  5. ch <- i
  6. for sum := range ch {
  7. fmt.Print("Sum(", i, ")=", sum)
  8. }
  9. fmt.Println()
  10. time.Sleep(time.Second)
  11. close(f)
  12. }
  13. }

f 通道是一个信号通道,用来当真正的工作完成时结束 goroutine。ch := <-cc 表达式允许您从传递通道的通道获得一个常规的通道,使用 ch <-i 发送一个 int 值给它。之后,您使用 for 循环从它读取数据。尽管 f1() 函数编写为发送一个值回来,但您也可以读取多个值。注意 i 的每个值都被不同的 goroutine 执行。

信号通道的类型可以是任何您想要的类型,包括上面用到的 bool 和下节要用到的 struct{} 类型的信号通道。一个 struct{} 信号通道的优点是不能发送数据给它,这能减少错误和错误的想法。

执行 chSquare.go 将产生如下输出:

  1. $go run chSquare.go 4
  2. Sum(1)=1
  3. Sum(2)=3
  4. Sum(3)=6
  5. Sum(4)=10
  6. $go run chSquare.go 6
  7. Sum(1)=1
  8. Sum(2)=3
  9. Sum(3)=6
  10. Sum(4)=10
  11. Sum(5)=15
  12. Sum(6)=21