上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个值,写入操作的完成无需等待读取端准备好。