select的使用方法很像switch语句,但是select没有输入值,只用于信道操作。
select用于多个信道的发送或者接收信道的操作中进行选择。
语句会阻塞直到其中有信道可以操作,若有多个信道可以操作,会随机选择其中一个case执行。
func service1(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from service1"
}
func service2(ch chan string) {
time.Sleep(1 * time.Second)
ch <- "from service2"
}
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go service1(ch1)
go service2(ch2)
select { // 会发送阻塞
case s1 := <-ch1:
fmt.Println(s1)
case s2 := <-ch2:
fmt.Println(s2)
}
}
//输出:
from service2
上面的函数执行到select语句时会发生阻塞,main主协程在等待一个case操作可执行,明显server2先准备好了写。
func server1(ch chan string) {
//time.Sleep(2 * time.Second)
ch <- "from service1"
}
func server2(ch chan string) {
//time.Sleep(1 * time.Second)
ch <- "from service2"
}
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go server1(ch1)
go server2(ch2)
time.Sleep(2*time.Second)
select {
case s1 := <-ch1:
fmt.Println(s1)
case s2 := <-ch2:
fmt.Println(s2)
}
}
这种情况下会随机执行一个case
default case:
与switch一样select也有default case,是的select语句不再阻塞,
如果其它信道没准备好,会直接执行default分支。
func server1(ch chan string) {
ch <- "from service1"
}
func server2(ch chan string) {
ch <- "from service2"
}
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go server1(ch1)
go server2(ch2)
select { // ch1 ch2 都还没有准备好,直接执行 default 分支
case s1 := <-ch1:
fmt.Println(s1)
case s2 := <-ch2:
fmt.Println(s2)
default:
fmt.Println("no case ok")
}
}
//输出:
no case ok
若在select前加一个延迟1s,则会随机执行case,不会执行default
nil channel:
若信道的默认值时nil,则不能对其进行读写操作。
func server1(ch chan string) {
ch <- "from service1"
}
func main() {
var ch chan string
go server1(ch)
select {
case str := <-ch:
fmt.Println(str)
}
}
//输出:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
goroutine 18 [chan send (nil chan)]:
[select (no cases)] case 分支中如果信道是 nil,该分支就会被忽略。
就变成空 select{} 语句,阻塞主协程,开始调度 service1 协程,
在 nil 信道上操作,便报[chan send (nil chan)] 错误,可以使用default case避免。
空select:
func main(){
select{}
}
//输出:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]
select语句会发生阻塞,直到case可以操作。但是空的select没有case分支,便会死锁