Select
select 语句用在多个发送/接收通道操作中进行选择。
例子:
程序创建了 3 个通道,并在执行 select
语句之前往通道 1 、通道 2 和 通道 3 分别发送数据,在执行 select
语句时,如果有机会的话会运行所有表达式,只要其中一个通道接收到数据,那么就会执行对应的 case
代码,然后退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package main
import "fmt"
func main() { ch1 := make(chan string, 1) ch2 := make(chan string, 1) ch3 := make(chan string, 1)
ch1 <- "111" ch2 <- "222" ch3 <- "333"
select { case msg1 := <-ch1: fmt.Println("ch1 received: ", msg1) case msg2 := <-ch2: fmt.Println("ch2 received: ", msg2) case msg3 := <-ch3: fmt.Println("ch3 received: ", msg3)
} }
|
select 的应用
每个任务执行的时间不同,使用 select
语句等待相应的通道发出响应。select
会选择首先响应先完成的 task,而忽略其它的响应。使用这种方法,我们可以做多个 task,并给用户返回最快的 task 结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package main
import ( "fmt" "time" )
func task1(ch chan string) { time.Sleep(5 * time.Second) ch <- "111" }
func task2(ch chan string) { time.Sleep(7 * time.Second) ch <- "222" }
func task3(ch chan string) { time.Sleep(2 * time.Second) ch <- "333" }
func main() { ch1 := make(chan string) ch2 := make(chan string) ch3 := make(chan string) go task1(ch1) go task2(ch2) go task3(ch3)
select { case message1 := <-ch1: fmt.Println("ch1 received:", message1) case message2 := <-ch2: fmt.Println("ch2 received:", message2) case message3 := <-ch3: fmt.Println("ch3 received:", message3) } }
|
因为如果加了该默认分支,如果还没从通道接收到数据, select
语句就会直接执行 default
分支然后退出,而不是被阻塞。
当 select
由多个 case
准备就绪时,将会随机地选取其中之一去执行。
select 超时处理
当 case
里的通道始终没有接收到数据时,而且也没有 default
语句时, select
整体就会阻塞,但是有时我们并不希望 select
一直阻塞下去,这时候就可以手动设置一个超时时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package main
import ( "fmt" "time" )
func makeTimeout(ch chan bool, t int) { time.Sleep(time.Second * time.Duration(t)) ch <- true }
func main() { c1 := make(chan string, 1) c2 := make(chan string, 1) c3 := make(chan string, 1) timeout := make(chan bool, 1)
go makeTimeout(timeout, 2)
select { case msg1 := <-c1: fmt.Println("c1 received: ", msg1) case msg2 := <-c2: fmt.Println("c2 received: ", msg2) case msg3 := <-c3: fmt.Println("c3 received: ", msg3) case <-timeout: fmt.Println("Timeout, exit.") } }
|
读取/写入数据
select
里的 case
表达式只能对通道进行操作,不管你是往通道写入数据,还是从通道读出数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main
import ( "fmt" )
func main() { c1 := make(chan string, 2)
c1 <- "111" select { case c1 <- "222": fmt.Println("c1 received: ", <-c1) fmt.Println("c1 received: ", <-c1) default: fmt.Println("channel blocking") } }
|