什么是select?
select
语句用于在多个发送/接收信道操作中进行选择。select
语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select
会随机地选取其中之一执行。该语法与switch
类似,不同的是,这里的每个case
语句都是信道操作。
select的应用
假设我们有一个关键性应用,需要尽快地把输出返回给用户。这个应用的数据复制并且存储在世界各地的服务器上。假设函数server1
和server2
与这样不同区域的两台服务器进行通信。每台服务器的负载和网络延时决定了它的响应时间。我们向两台服务器发送请求,并使用select
语句等待相应的信道发出响应。select
会选择首先响应的服务器,而忽略其它的响应。使用这种方法,我们可以向多个服务器发送请求,并给用户返回最快的响应!
默认情况
在没有case准备就绪时,可以执行select
语句中的默认情况,这通常用于防止select
语句一直阻塞。
package main
import (
"fmt"
"time"
)
func process(ch chan string) {
time.Sleep(10500 * time.Millisecond)
ch <- "process successful"
}
func main() {
ch := make(chan string)
go process(ch) // 并发地调用该函数
for {
time.Sleep(1000 * time.Millisecond)
select {
case v := <-ch:
fmt.Println("received value:", v)
return
default:
fmt.Println("no value received")
}
}
// output:
// no value received
// no value received
// no value received
// no value received
// no value received
// no value received
// no value received
// no value received
// no value received
// no value received
// received value: process successful
}
死锁与默认情况
package main
func main() {
ch := make(chan string)
select {
case <-ch: // 由于没有Go协程向该信道写入数据,select语句会一直阻塞,导致死锁
// 可以在这里添加default,就不会发生死锁
}
}
随机选取
当select
中有多个case准备就绪时,将会随机选取其中之一去执行。
空select
package main
func main() {
select{}
}
我们已经知道,除非有case执行,否则select语句就会一直阻塞着。在上述代码,select语句没有任何case,因此它会一直阻塞,导致死锁。
reference: