前言
select是go语言当中提供的一个选择语句。select的语法类似switch语句,也属于控制语句。
那为什么select语句我们没有放在和if、switch语句一起?
因为select是配合channel通道使用的。每个 case 必须是一个通信操作,要么是发送要么是接收。
特性总结
- select只能用于channel操作,每个case都必须是一个channel;
- 如果有多个case可以允许(channel没有阻塞),则随机选择一条case语句执行;
- 如果没有case语句可以执行(channel发生阻塞),切没有default语句,则select语句会阻塞;
- 如果没有case语句可以执行(channel发生阻塞),有default语句,则执行default语句;
- 一般使用超时语句代替default语句;
- 如果case语句中的channel为nil,则忽略该分支,相当于从select中删除了该分支;
- 如果select语句在for循环中,一般不使用default语句,因为会引起CPU占用过高问题。
select语句
select语法格式
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
示例
select只能用于channel操作,每个case都必须是一个channel。且channel是读取/写入都可以。
func main() {
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
ch1 <- 100
}()
go func() {
num2 := <-ch2
fmt.Println(num2)
}()
select {
case num1 := <-ch1: //读取channel数据
fmt.Println("ch1中的数据是:", num1)
case ch2 <- "201": //channel写入数据
fmt.Println("ch2有数据写入")
}
}
随机性
如果多条case语句都可以执行,则随机执行一条。
示例中,两条case语句都可以执行。多次执行可以发现,case语句是随机执行其中一条的。
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
ch1 <- 100
}()
go func() {
ch2 <- 200
}()
select {
case num1 := <-ch1:
fmt.Println("ch1中的数据是:", num1)
case num2 := <-ch2:
fmt.Println("ch2中的数据是:", num2)
}
}
死锁
如果所有case语句都被阻塞,切没有default语句和超时语句,则程序会报错死锁。
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
select {
case <-ch1:
fmt.Println("ch1")
case <-ch2:
fmt.Println("ch2")
}
}
default语句
如果所有case语句都阻塞,有default语句的话,就会执行default语句。
要注意如果select语句在for循环中,default语句可能会造成CPU占用过高。
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
select {
case num1 := <-ch1:
fmt.Println("ch1中的数据是:", num1)
case num2 := <-ch2:
fmt.Println("ch2中的数据是:", num2)
default:
fmt.Println("通道阻塞...default")
}
}
超时语句
一般使用超时语句代替default语句。
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
select {
case num1 := <-ch1:
fmt.Println("ch1中的数据是:", num1)
case num2 := <-ch2:
fmt.Println("ch2中的数据是:", num2)
case <-time.After(3 * time.Second):
fmt.Println("timeout...")
}
}