Go 非阻塞通道

优质
小牛编辑
133浏览
2023-12-01

默认情况下,通道发送和接收数据是阻塞的。然而我们可以使用select的一个default的选项来实现无阻塞发送或接收数据,甚至可以将多个select的case选项和default选项结合起来使用。

  1. package main
  2. import "fmt"
  3. func main() {
  4. messages := make(chan string)
  5. signals := make(chan bool)
  6. // 这里是一个非阻塞的从通道接收数据,如果messages通道有数据
  7. // 可以接收,那么select将运行`<-messages`这个case,否则的话
  8. // 程序立刻执行default选项后面的语句
  9. select {
  10. case msg := <-messages:
  11. fmt.Println("received message", msg)
  12. default:
  13. fmt.Println("no message received")
  14. }
  15. // 非阻塞通道发送数据也是一样的
  16. msg := "hi"
  17. select {
  18. case messages <- msg:
  19. fmt.Println("sent message", msg)
  20. default:
  21. fmt.Println("no message sent")
  22. }
  23. // 在default前面,我们可以有多个case选项,从而实现多通道
  24. // 非阻塞的选择,这里我们尝试从messages和signals接收数据
  25. // 如果有数据可以接收,那么执行对应case后面的逻辑,否则立刻
  26. // 执行default选项后面的逻辑
  27. select {
  28. case msg := <-messages:
  29. fmt.Println("received message", msg)
  30. case sig := <-signals:
  31. fmt.Println("received signal", sig)
  32. default:
  33. fmt.Println("no activity")
  34. }
  35. }

运行结果

  1. no message received
  2. no message sent
  3. no activity

这个例子中,由于我们使用了default来实现非阻塞的通道,所以开始的时候messages里面没有数据可以接收,直接输出no message received,而第二次由于messages通道没有相应的数据接收方,所以也不会写入数据,直接转到default,输出no message sent,至于第三个就很好理解了,什么也没有,输出no activity
其实,我们可以把这个例子修改一下,让messages通道带缓冲区,这样例子或许更好理解一点。定义messages的时候使用messages := make(chan string, 1)

  1. package main
  2. import "fmt"
  3. func main() {
  4. messages := make(chan string, 1)
  5. signals := make(chan bool)
  6. // 这里是一个非阻塞的从通道接收数据,如果messages通道有数据
  7. // 可以接收,那么select将运行`<-messages`这个case,否则的话
  8. // 程序立刻执行default选项后面的语句
  9. select {
  10. case msg := <-messages:
  11. fmt.Println("received message", msg)
  12. default:
  13. fmt.Println("no message received")
  14. }
  15. // 非阻塞通道发送数据也是一样的,但是由于messages带了缓冲区,
  16. // 即使没有数据接受端也可以发送数据,所以这里的`messages<-msg`
  17. // 会被执行,从而不再跳到default去了。
  18. msg := "hi"
  19. select {
  20. case messages <- msg:
  21. fmt.Println("sent message", msg)
  22. default:
  23. fmt.Println("no message sent")
  24. }
  25. // 在default前面,我们可以有多个case选项,从而实现多通道
  26. // 非阻塞的选择,这里我们尝试从messages和signals接收数据
  27. // 如果有数据可以接收,那么执行对应case后面的逻辑,否则立刻
  28. // 执行default选项后面的逻辑
  29. select {
  30. case msg := <-messages:
  31. fmt.Println("received message", msg)
  32. case sig := <-signals:
  33. fmt.Println("received signal", sig)
  34. default:
  35. fmt.Println("no activity")
  36. }
  37. }

运行结果

  1. no message received
  2. sent message hi
  3. received message hi