22、go语言select语句块
转载:https://www.jianshu.com/p/2a1146dc42c3
1、基本用法
select是用来监听和channel有关的IO操作,当IO操作发生时,触发对应的动作
select {
case <- chan1:
//如果chan1成功读取到数据,则进行该case处理语句
case chan2 <- 2:
//若成功向chan2写入数据,则进行该case处理语句
default:
//如果上面都没有成功, 则进入default处理语句(default选填)
}
2、简介
所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。
结果是选择一个发送或接收的channel,无论选择哪一个case进行操作,表达式都会被执行。
如果有一个或多个IO操作可以完成,则go会随机选择一个执行,否则的话,如果有default分支,则执行default分支,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行
3、example 1
如果有一个或多个IO操作可以完成,则GO运行时系统会随机的选择一个执行,否则,若有default分支,则执行default分支,如果连default分支都没有,则select语句会一直阻塞,直到至少有一个IO操作可以运行
start := time.Now()
c := make(chan interface{})
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep( 4 * time.Second)
close(c)
}()
go func() {
time.Sleep( 3 * time.Second)
ch1 <- 3
}()
go func() {
time.Sleep( 3 * time.Second)
ch2 <- 5
}()
fmt.Println("Blocking on read:")
select {
case <- c:
fmt.Printf("Unblocked %v later.\n", time.Since(start))
case <- ch1:
fmt.Printf("ch1 case...\n")
case <- ch2:
fmt.Printf("ch2 case ...\n")
default:
fmt.Printf("default go...")
}
运行上述代码,由于当前时间未到3s,所以,程序会走default
Blocking on read:
default go...
Process finished with exit code 0
修改代码,将default注释掉:
//default:
//fmt.Printf("default go...")
这时,select语句会阻塞,直到检测到一个可以执行的IO操作为止,这里先会执行完睡眠3s的gorountine,此时两个channel都满足条件,这时系统会随机选择一个case继续操作
Blocking on read:
ch2 case ...
Process finished with exit code 0
接着,继续修改代码,将ch1和ch2的goroutine休眠时间改为5S:
go func() {
time.Sleep( 5 * time.Second)
ch1 <- 3
}()
go func() {
time.Sleep( 5 * time.Second)
ch2 <- 5
}()
此时会先执行到上面的gorountine,select执行的就是c的case
Blocking on read:
Unblocked 4.0008118s later.
Process finished with exit code 0
4、example 4
所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下,从左到右:
var ch1 chan int
var ch2 chan int
var chs = []chan int{ch1, ch2}
var numbers = []int{1, 2, 3, 4, 5}
func getNumber(i int) int {
fmt.Printf("numbers[%d]\n", i)
return numbers[i]
}
func getChan(i int) chan int {
fmt.Printf("chs[%d]\n", i)
return chs[i]
}
func chanOrderDemo() {
select {
case getChan(0) <- getNumber(2):
fmt.Println("1th case is selected.")
case getChan(1) <- getNumber(3):
fmt.Println("2th case is selected.")
default:
fmt.Println("default!.")
}
}
此时,select语句走的是default操作,但是这是每个case表达式都会被执行:以case1为例:
case getChan(0) <- getNumber(2):
系统会从左到右先执行getChan(0)
并打印chs[0]执行
,然后执行 getNumber(2)
函数打印numbers[2]
。同样,从上到下分别执行所有case的语句。所以执行结果为:
chs[0]
numbers[2]
chs[1]
numbers[3]
default!.
Process finished with exit code 0
5、example 5
break关键字结束select
func selectUseBreak(){
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch1 <- 3
ch2 <- 5
select {
case <- ch1:
fmt.Println("ch1 selected")
break
fmt.Println("ch1 selected after break")
case <- ch2:
fmt.Println("ch2 selected.")
fmt.Println("ch2 selected without break")
}
}
很明显,ch1和ch2两个通道都可以读取到值,所以系统会随机选择一个case执行。我们发现选择执行ch1的case时,由于有break关键字,所以只执行了一句:
ch1 selected
Process finished with exit code 0
但是,当系统选择ch2的case时,打印结果为:
ch2 selected.
ch2 selected without break
Process finished with exit code 0