1. select

1.1.1. select多路复用

在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现:

  1. for{
  2. // 尝试从ch1接收值
  3. data, ok := <-ch1
  4. // 尝试从ch2接收值
  5. data, ok := <-ch2
  6. }

这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,Go内置了select关键字,可以同时响应多个通道的操作。

select的使用类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的语句。具体格式如下:

  1. select {
  2. case <-chan1:
  3. // 如果chan1成功读到数据,则进行该case处理语句
  4. case chan2 <- 1:
  5. // 如果成功向chan2写入数据,则进行该case处理语句
  6. default:
  7. // 如果上面都没有成功,则进入default处理流程
  8. }
  • select可以同时监听一个或多个channel,直到其中一个channel ready
  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func test1(ch chan string) {
  7. time.Sleep(time.Second * 5)
  8. ch <- "test1"
  9. }
  10. func test2(ch chan string) {
  11. time.Sleep(time.Second * 2)
  12. ch <- "test2"
  13. }
  14. func main() {
  15. // 2个管道
  16. output1 := make(chan string)
  17. output2 := make(chan string)
  18. // 跑2个子协程,写数据
  19. go test1(output1)
  20. go test2(output2)
  21. // 用select监控
  22. select {
  23. case s1 := <-output1:
  24. fmt.Println("s1=", s1)
  25. case s2 := <-output2:
  26. fmt.Println("s2=", s2)
  27. }
  28. }
  • 如果多个channel同时ready,则随机选择一个执行
  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. // 创建2个管道
  7. int_chan := make(chan int, 1)
  8. string_chan := make(chan string, 1)
  9. go func() {
  10. //time.Sleep(2 * time.Second)
  11. int_chan <- 1
  12. }()
  13. go func() {
  14. string_chan <- "hello"
  15. }()
  16. select {
  17. case value := <-int_chan:
  18. fmt.Println("int:", value)
  19. case value := <-string_chan:
  20. fmt.Println("string:", value)
  21. }
  22. fmt.Println("main结束")
  23. }
  • 可以用于判断管道是否存满
  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. // 判断管道有没有存满
  7. func main() {
  8. // 创建管道
  9. output1 := make(chan string, 10)
  10. // 子协程写数据
  11. go write(output1)
  12. // 取数据
  13. for s := range output1 {
  14. fmt.Println("res:", s)
  15. time.Sleep(time.Second)
  16. }
  17. }
  18. func write(ch chan string) {
  19. for {
  20. select {
  21. // 写数据
  22. case ch <- "hello":
  23. fmt.Println("write hello")
  24. default:
  25. fmt.Println("channel full")
  26. }
  27. time.Sleep(time.Millisecond * 500)
  28. }
  29. }