channel 的 range 和 select 比较
洪笳淏 Lv4

本文是对 Channels in Go - range and select 的学习

range

  在 goroutine 之间使用 channel 通信时,数据接收方总是面临这样的问题:什么时候停止等待数据?还会有更多的数据么,还是所有内容都完成了?我应该继续等待还是该做别的了?对于这个问题,一个可选的方式是,持续的访问数据源并检查channel是否已经关闭,但是这并不是高效的解决方式。Go提供了range关键字,将其使用在channel上时,会自动等待channel的动作一直到channel被关闭。

下面通过一段示例代码来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main  

import (
"fmt"
)

func a(send chan<- int, num int) {
for i:=1; i<=num; i++ {
send <- i
}
close(send)
}

func b(recieve <-chan int) {
for num := range recieve {
fmt.Printf("Now we recieve the number %v\n", num)
}
}

func main() {
c := make(chan int)
go a(c, 8)
b(c)
}
1
2
3
4
5
6
7
8
9
输出结果
Now we recieve the number 1
Now we recieve the number 2
Now we recieve the number 3
Now we recieve the number 4
Now we recieve the number 5
Now we recieve the number 6
Now we recieve the number 7
Now we recieve the number 8

a 这个 goroutine 一直向 channel 内传递数据,直到这个 channel 被关闭前,b 这个 goroutine 都会通过 range 这个关键字阻塞在循环中一直等待 channel 中的数据。通过这个例子还可以看出,for range 可以接收 channel 中的数据,不然 c 作为一个无缓冲 channel,a 不可能向其中一直发送数据。

select

select关键字用于多个channel的结合,这些channel会通过类似于 are-you-ready polling 的机制来工作。select中会有case代码块,用于发送或接收数据——不论通过<-操作符指定的发送还是接收操作准备好时,channel也就准备好了。在select中也可以有一个default代码块,其一直是准备好的。那么,在select中,哪一个代码块被执行的算法大致如下:

  • 检查每个case代码块
  • 如果任意一个case代码块准备好发送或接收,执行对应内容
  • 如果多于一个case代码块准备好发送或接收,随机选取一个并执行对应内容
  • 如果任何一个case代码块都没有准备好,等待
  • 如果有default代码块,并且没有任何case代码块准备好,执行default代码块对应内容
  • Post title:channel 的 range 和 select 比较
  • Post author:洪笳淏
  • Create time:2022-03-19 18:14:00
  • Post link:https://jiahaohong1997.github.io/2022/03/19/channel 的 range 和 select 比较/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
 Comments