基本使用
select 是 Go 中的一个控制结构, 类似于switch 语句, 用于处理异步 IO 操作. select 语句会监听 case语句中channel 的读写操作, 当case 中 channel 读写操作为非阻塞状态(即能读写)时, 将会触发相应的动作.
select 中的 case 语句必须是一个 channel 操作
select 中的 default 子句总是可运行的
- 如果有多个 case 都可以运行, select 会随机公平地选出一个执行, 其他不会执行
- 如果没有可运行的 case 语句, 且有 default 语句, 则会执行 default 的动作
- 如果没有可运行的 case 语句, 且没有 default 语句, select 将阻塞, 知道某个 case 通信可以运行
例
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
}
//输出:no communication
典型用法
1. 超时判断
//比如在下面的场景中,使用全局resChan来接受response,如果时间超过3S,resChan中还没有数据返回,则第二条case将执行
var resChan = make(chan int)
// do request
func test() {
select {
case data := <-resChan:
doData(data)
case <-time.After(time.Second * 3):
fmt.Println("request time out")
}
}
func doData(data int) {
//...
}
2. 退出
//主线程(协程)中如下:
var shouldQuit=make(chan struct{})
fun main(){
{
//loop
}
//...out of the loop
select {
case <-c.shouldQuit:
cleanUp()
return
default:
}
//...
}
//再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行
close(shouldQuit)
3. 判断 channel 是否阻塞
//在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断
ch := make (chan int, 5)
//...
data:=0
select {
case ch <- data:
default:
//做相应操作,比如丢弃data。视需求而定
}