李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
24.Golang之select
Leefs
2022-07-14 PM
2058℃
0条
[TOC] ### 前言  ### 一、概述 select 是 Golang 中的一个控制结构,语法上类似于switch 语句,只不过select是用于 goroutine 间通信的 ,每个 case 必须是一个通信操作,要么是发送要么是接收,select 会随机执行一个可运行的 case。如果没有 case 可运行,goroutine 将阻塞,直到有 case 可运行。 ### 二、基本语法 **语法** ```go //select基本用法 select { case <- chan1: // 如果chan1成功读到数据,则进行该case处理语句 case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句 default: // 如果上面都没有成功,则进入default处理流程 } ``` **注意** + 如果没有``default``分支,``select``会阻塞在多个channel上,对多个channel的读/写事件进行监控。 + 如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,否则的话,如果有``default``分支,则执行``default``分支语句,如果连``default``都没有,则``select``语句会一直阻塞,直到至少有一个IO操作可以进行。 ### 三、Select使用 #### 3.1 同时从多个通道中接收数据 ```go import ( "fmt" "time" ) func main() { //1.定义一个通道 10个数据int intChan := make(chan int, 10) for i := 0; i < 10; i++ { intChan <- i } //2.定义一个通道 5个数据string stringChan := make(chan string, 5) for i := 0; i < 5; i++ { stringChan <- "hello" + fmt.Sprintf("%d", i) } //使用select来获取channel里面的数据的时候不需要关闭channel for { select { case v := <-intChan: fmt.Printf("从 intChan 读取的数据%d\n", v) time.Sleep(time.Millisecond * 50) case v := <-stringChan: fmt.Printf("从 stringChan 读取的数据%v\n", v) time.Sleep(time.Millisecond * 50) default: fmt.Printf("数据获取完毕") return //注意退出... } } } ``` **运行结果** ``` 从 stringChan 读取的数据hello0 从 stringChan 读取的数据hello1 从 stringChan 读取的数据hello2 从 intChan 读取的数据0 从 stringChan 读取的数据hello3 从 intChan 读取的数据1 从 stringChan 读取的数据hello4 从 intChan 读取的数据2 从 intChan 读取的数据3 从 intChan 读取的数据4 从 intChan 读取的数据5 从 intChan 读取的数据6 从 intChan 读取的数据7 从 intChan 读取的数据8 从 intChan 读取的数据9 数据获取完毕 ``` #### 3.2 造成死锁 当程序中不使用`default` 分支,如果一直没有命中其中的某个 `case` 最后会造成死锁。 ```go import "fmt" func main() { // 创建三个通道 ch1 := make(chan string, 1) ch2 := make(chan string, 1) ch3 := make(chan string, 1) select { case message1 := <-ch1: // 如果从通道 1 收到数据 fmt.Println("ch1 received:", message1) case message2 := <-ch2: // 如果从通道 2 收到数据 fmt.Println("ch2 received:", message2) case message3 := <-ch3: // 如果从通道 3 收到数据 fmt.Println("ch3 received:", message3) } } ``` **运行报错** ``` fatal error: all goroutines are asleep - deadlock! goroutine 1 [select]: main.main() D:/Codes/gocode/godemo/base11/test02.go:11 +0xfd ``` 运行上面的程序会造成死锁。解决该问题的方法是写好 `default` 分支。 **空 select造成死锁** ```go package main func main() { select {} //fatal error: all goroutines are asleep - deadlock! } ``` 空`select` 语句没有任何 `case`,因此它会一直阻塞,导致死锁。 #### 3.3 select 超时处理 当 `case` 里的通道始终没有接收到数据时,而且也没有 `default` 语句时, `select` 整体就会阻塞,但是有时我们并不希望 `select` 一直阻塞下去,这时候就可以手动设置一个超时时间。 ```go import ( "fmt" "time" ) func makeTimeout(ch chan bool, t int) { time.Sleep(time.Second * time.Duration(t)) ch <- true } func main() { c1 := make(chan string, 1) c2 := make(chan string, 1) c3 := make(chan string, 1) timeout := make(chan bool, 1) go makeTimeout(timeout, 2) select { case msg1 := <-c1: fmt.Println("c1 received: ", msg1) case msg2 := <-c2: fmt.Println("c2 received: ", msg2) case msg3 := <-c3: fmt.Println("c3 received: ", msg3) case <-timeout: fmt.Println("Timeout, exit.") } } ``` **运行结果** ``` Timeout, exit. ``` ### 总结 1. select语句只能用于信道的读写操作; 2. select中的case条件(非阻塞)是并发执行的,select会选择先操作成功的那个case条件去执行,如果多个同时返回,则随机选择一个执行,此时将无法保证执行顺序。对于阻塞的case语句会直到其中有信道可以操作,如果有多个信道可操作,会随机选择其中一个 case 执行; 3. 对于case条件语句中,如果存在信道值为nil的读写操作,则该分支将被忽略,可以理解为从select语句中删除了这个case语句; 4. 如果有超时条件语句,判断逻辑为如果在这个时间段内一直没有满足条件的case,则执行这个超时case。如果此段时间内出现了可操作的case,则直接执行这个case。一般用超时语句代替了default语句; 5. 对于空的select{},会引起死锁; 6. 对于for中的select{}, 也有可能会引起cpu占用过高的问题。 *附参考文章链接* *http://www.go-edu.cn/2022/05/27/go-16-select/*
标签:
Golang基础
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2252.html
上一篇
23.Golang之Channel示例
下一篇
25.Golang并发安全和锁
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
35
其它
25
GO
48
NLP
8
标签云
Thymeleaf
NIO
Ray
Azkaban
Zookeeper
Ubuntu
ajax
nginx
JavaWEB项目搭建
Python
算法
GET和POST
SpringCloud
序列化和反序列化
Spark
pytorch
国产数据库改造
Quartz
排序
MySQL
线程池
CentOS
gorm
SpringBoot
锁
正则表达式
Java编程思想
队列
DataX
链表
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞