化茧成蝶:Go在FreeWheel服务化中的实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

Channel的数据读取

在前面讲到Channel定义时我们介绍过从Channel中读取数据的最简单方式。其实在实际程序中,读取Channel往往是用于监听一些状态的变化和感知从其他Goroutine来的数据传递,既然使用了Goroutine并发地去运行程序,我们自然是希望各个Goroutine不受阻塞地做各自的处理,只有在监听的Channel里有当前Goroutine关心的数据到达时,再做相应的处理。所以Channel的读取往往就不是简单地使用了,需要做一些包装和改进来更好地利用其在Goroutine之间同步的能力。

从Channel中读取数据的方式

直接读取

前面总结过,直接读取即用Channel的读取操作符<-直接从Channel中读值,例如:

        <- inbox

当Channel无Buffer,或Buffer中没有数据时,程序会在此处阻塞,直到有数据可读。在实际的Go程序中,还是有几种常用的模式来从Channel中读取数据的。对于一个同步执行的Goroutine来说,从Channel读取的场景包括程序等待同步状态、监听通知、获取数据等等。

用select读取

最常见的监听某个Channel的方式莫过于另起一个Goroutine,在当中用for循环做select操作,循环地从Channel中读取数据,例如:

        func main() {
            ch1 := make(chan int)
              go func() {
                    for {
                          select {
                          case val := <-ch1:
                                  fmt.Println("Received value: ",  val)
                          }
                    }
              }()
              for i := 0;  i <= 10;  i++ {
                    ch1 <- i * 2
              }
              fmt.Println("Process done.")
        }

用for循环读取

用for循环读取,无Buffer的Channel行为与select方式一致,如下所示。读取无Buffer的Channel:

        func main() {
          ch1 := make(chan int)
              go func() {
                    fmt.Println("-- start read")
                    for r := range ch1 {

                          fmt.Println("Received value: ",  r)
                    }
              }()
              for i := 0;  i <= 10;  i++ {
                    ch1 <- i * 2
              }
              fmt.Println("Process done.")
        }

读取有Buffer的Channel

        func main() {
            ch1 := make(chan int,  20)
              for i := 0;  i <= 10;  i++ {
                    ch1 <- i * 2
              }
              go func() {
                    l := len(ch1)
                    fmt.Println("-- start read,  the length of channel is: ",
        l)
                    for r := range ch1 {
                          fmt.Printf("Received value: %d,  the length is:
        %d\n",  r,  len(ch1))
                    }
                    fmt.Println("Drain channel out done.")
              }()
                time.Sleep(5*100*time.Millisecond)
              fmt.Println("Process done.")
        }

区别在于,有Buffer的Channel在Buffer未满之前写入是不会阻塞的,所以这段示例代码可以先往Channel中写入10个值,写入操作的完成无需等待读取端准备好。