为了应对同时从多个通道接收数据,Go内置了select关键字

1. select的作用

select类似switch,它有一系列的分支和默认分支,每一个case必须是一个通道的通信过程(接收或发送)。执行select后一直阻塞,直到其中一个case的通信操作完成时,它会执行case分支的对应语句,执行完后退出select

例子:

  1. ch1 := make(chan int, 1)
  2. ch2 := make(chan int, 1)
  3. ch1 <- 1
  4. ch2 <- 2
  5. select {
  6. case s := <-ch1:
  7. fmt.Println(s)
  8. case s := <-ch2:
  9. fmt.Println(s)
  10. }

2. case的执行顺序

看了上面的例子,可能会想select的case是按顺序执行的?先不着急下结论,再写个例子执行看看

例子:

  1. ch1 := make(chan int, 11)
  2. ch2 := make(chan int, 11)
  3. for i := 0; i < 10; i++ {
  4. ch1 <- 1
  5. ch2 <- 2
  6. }
  7. for i := 0; i < 10; i++ {
  8. select {
  9. case s := <-ch1:
  10. fmt.Println(s)
  11. case s := <-ch2:
  12. fmt.Println(s)
  13. }
  14. }

打印输出:
image.png
嗯,不是我们想象的1、2轮流打印。得出的结论:当同时有多个case的通道通信操作完成时,select的case不是顺序执行的,而是随机选择一个执行。

3. for select

大多时候我们会使用for结合select使用,需要注意一下如何退出for循环

  1. func main() {
  2. wg := &sync.WaitGroup{}
  3. wg.Add(1)
  4. test(wg)
  5. wg.Wait()
  6. }
  7. func test(wg *sync.WaitGroup) {
  8. ch := make(chan int)
  9. for i := 1; i < 6; i++ {
  10. index := i
  11. go func(index int) {
  12. ch <- index
  13. }(index)
  14. }
  15. count := 0
  16. EXIT:
  17. for {
  18. select {
  19. case index := <-ch:
  20. fmt.Println(index)
  21. count++
  22. if count == 5 {
  23. // 通过break标签退出for循环
  24. break EXIT
  25. }
  26. }
  27. }
  28. fmt.Println("end...")
  29. wg.Done()
  30. }