前端尝试转GO学习(第八天)
select语句用来选择哪个case中的发送或接收操作可以被立即执行。
类似于switch语句,但是它的case涉及到channel有关的I/O操作。
即select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。
select语句会一直阻塞,直到发送/接收操作准备就绪。- 如果有多个通道操作准备完毕,
select会随机地选取其中之一执行。 - 否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行。
select 语法如下:
select {
case expression1:
code
case expression2:
code
default:
code
}
- 每个 case 都必须是一个通信
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以执行,它就会执行;其他就会被忽略
- 如果有多个 case 都可以运行,select 会随机公平的选出一个执行。其他不会执行。
func main() {
// 创建通道
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
ch3 := make(chan string, 1)
// 发送数据
ch1 <- "React"
ch2 <- "Vue"
ch3 <- "Go"
// 接收数据
select {
case message1 := <-ch1:
fmt.Println("ch1: ", message1)
case message2 := <-ch2:
fmt.Println("ch2: ", message2)
case message3 := <-ch3:
fmt.Println("ch3: ", message3)
default:
fmt.Println(" no")
}
}
创建了 3 个通道,往3个通道分别发送数据。只要其中一个通道接收到数据,那么就会执行对应的 case 代码。
ch1: React
ch3: Vue
ch3: Go
阻塞
func main() {
// 创建通道
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
ch3 := make(chan string, 1)
// 接收数据
select {
case message1 := <-ch1:
fmt.Println("ch1: ", message1)
case message2 := <-ch2:
fmt.Println("ch2: ", message2)
case message3 := <-ch3:
fmt.Println("ch3: ", message3)
}
}
会造成死锁,fatal error: all goroutines are asleep - deadlock!
select 的应用
每个任务执行的时间不同,使用 select 语句等待相应的通道发出响应。
select 会选择首先响应先完成的 task,忽略其它。
这种情况下可以做多个 task,并给用户返回最快的 task 结果。
(有点类似于 JS 中的 Promise.race)
func task1(ch chan string) {
time.Sleep(1 * time.Second)
ch <- "task 1"
}
func task2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "task 2"
}
func task3(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "task 3"
}
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:", message1)
case message2 := <-ch2:
fmt.Println("ch2:", message2)
case message3 := <-ch3:
fmt.Println("ch3:", message3)
}
}
Fibonacci 数列
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}