在golang语言中,select语句 就是用来监听和channel有关的IO操作,当IO操作发生时,触发相应的case动作。有了 select语句,可以实现 main主线程 与 goroutine线程 之间的互动。
select {
case <-ch1 : // 检测有没有数据可读
// 一旦成功读取到数据,则进行该case处理语句
case ch2 <- 1 : // 检测有没有数据可写
// 一旦成功向ch2写入数据,则进行该case处理语句
default:
// 如果以上都没有符合条件,那么进入default处理流程
}
注意事项:
select语句 只能用于channel信道的IO操作,每个case都必须是一个信道。
如果不设置 default条件,当没有IO操作发生时,select语句就会一直阻塞;
如果有一个或多个IO操作发生时,Go运行时会随机选择一个case执行,但此时将无法保证执行顺序;
对于case语句,如果存在信道值为nil的读写操作,则该分支将被忽略,可以理解为相当于从select语句中删除了这个case;
对于空的 select语句,会引起死锁;
对于在 for中的select语句,不能添加 default,否则会引起cpu占用过高的问题;
先创建两个信道,并在 select 前往 c2 发送数据
package main
import (
"fmt"
)
//go的通道选择器 让你可以同时等待多个通道操作。go协程和通道以及选择器的结合是go的一个强大特性。
func main() {
// 在我们的例子中,我们将从两个通道中选择。
c1 := make(chan string, 1)
c2 := make(chan string, 1)
c2 <- "nihao"
//go func() {
// time.Sleep(time.Second * 1)
// c1 <- "one"
//}()
//
//go func() {
// time.Sleep(time.Second * 2)
// c2 <- "two"
//}()
//我们使用 `select` 关键字来同时等待这两个值,并打印各自接收到的值。
//for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
default:
fmt.Println("No data received")
}
//}
}
在运行 select 时,会遍历所有(如果有机会的话)的 case 表达式,只要有一个信道有接收到数据,那么 select 就结束,所以输出如下
select 在执行过程中,必须命中其中的某一分支。
如果在遍历完所有的 case 后,若没有命中(命中
:也许这样描述不太准确,我本意是想说可以执行信道的操作语句)任何一个 case 表达式,就会进入 default 里的代码分支。
package main
import (
"fmt"
)
//go的通道选择器 让你可以同时等待多个通道操作。go协程和通道以及选择器的结合是go的一个强大特性。
func main() {
// 在我们的例子中,我们将从两个通道中选择。
c1 := make(chan string, 1)
c2 := make(chan string, 1)
//c2 <- "nihao"
//go func() {
// time.Sleep(time.Second * 1)
// c1 <- "one"
//}()
//
//go func() {
// time.Sleep(time.Second * 2)
// c2 <- "two"
//}()
//我们使用 `select` 关键字来同时等待这两个值,并打印各自接收到的值。
//for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
//default:
// fmt.Println("No data received")
//}
}
}
但如果你没有写 default 分支,select 就会阻塞,直到有某个 case 可以命中,而如果一直没有命中,select 就会抛出deadlock
的错误,就像下面这样子。
1.解决这个问题的方法有两种
一个是,养成好习惯,在 select 的时候,也写好 default 分支代码,尽管你 default 下没有写任何代码。
另一个是,让其中某一个信道可以接收到数据
之前学过 switch 的时候,知道了 switch 里的 case 是顺序执行的,但在 select 里却不是。
通过下面这个例子的执行结果就可以看出
当 case 里的信道始终没有接收到数据时,而且也没有 default 语句时,select 整体就会阻塞,但是有时我们并不希望 select 一直阻塞下去,这时候就可以手动设置一个超时时间。
上面例子里的 case,好像都只从信道中读取数据,但实际上,select 里的 case 表达式只要求你是对信道的操作即可,不管你是往信道写入数据,还是从信道读出数据。
select 与 switch 原理很相似,但它的使用场景更特殊,学习了本篇文章,你需要知道如下几点区别:
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦