select 条件语句【GO 基础】
〇、select 简介
select 语句类似于 switch 语句,但是 select 会随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。默认的子句 default 应该总是可运行的。
操作系统中实现 IO 多路复用的命令 select、poll、epoll,主要通过起一个线程,来监听并处理多个文件描述符代表的 TCP 链接,用来提高处理网络读写请求的效率。而 Go 语言的 select 命令,是用来起一个 goroutine 协程监听多个 Channel(代表多个 goroutine)的读写事件,提高从多个 Channel 获取信息的效率。二者具体目标和实现不同,但本质思想都是相同的。
一、select 测试
1.1 当没有 case 可运行时
由于 Go 自带死锁检测机制,当发现当前协程(goroutine )没有机会被唤醒时,则会发生 panic:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select]:
main.main()
E:/test/main.go:8 +0x87
情况一:select 内容为空:
package main
func main() {
select {}
}
此时 goroutine 默认永远阻塞。
情况二:select 内容不为空,但没有可运行的 case:
package main
import "fmt"
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int)
select {
case <-ch1:
// 从有缓冲chan中读取数据,由于缓冲区没有数据且没有发送者,该分支会阻塞
fmt.Println("Received from ch")
case i := <-ch2:
// 从无缓冲chan中读取数据,由于没有发送者,该分支会阻塞
fmt.Printf("i is: %d", i)
}
}
1.2 select 的默认运行项 default
当没有其他可运行的 case 时,执行 default 分支:
package main
import "fmt"
func main() {
ch1 := make(chan int, 1)
select {
case <-ch1:
// 从有缓冲 chan 中读取数据,由于缓冲区没有数据且没有发送者,该分支会阻塞
fmt.Println("Received from ch")
default:
fmt.Println("this is default")
}
}
输出:
1.3 同时存在多个可执行的分支时,随机执行其中一个
如下三个分支均满足执行条件:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
select {
case val := <-ch:
fmt.Println("Received from ch1, val =", val)
case val := <-ch:
fmt.Println("Received from ch2, val =", val)
case val := <-ch:
fmt.Println("Received from ch3, val =", val)
default:
fmt.Println("Run in default")
}
}
如下图连续运行了四次,可以看出执行的 case 并不固定:

二、几个典型用法
2.1 超时判断
如下代码,使用全局 resChan 来接受 response,如果时间超过 3s,resChan 中还没有数据返回,则第二条 case 将执行。
package main
import (
"fmt"
"time"
)
func main() {
test()
}
var resChan = make(chan int)
func test() {
select {
case data := <-resChan:
fmt.Println("data:", data)
case <-time.After(time.Second * 3): // 配置超时时间 3 秒钟
fmt.Println("request time out")
}
}
结果输出:
2.2 根据信号退出运行
如下代码,在协程中操作关闭通道,主线程监控到后退出:
package main
import (
"fmt"
"time"
)
var shouldQuit = make(chan struct{})
func main() {
fmt.Println("main...")
go quit() // 另开一个协程 goroutine
select {
case <-shouldQuit:
fmt.Println("shouldQuit...")
return
case <-time.After(time.Second * 2): // 超时时调用
fmt.Println("request time out")
}
}
func quit() {
time.Sleep(1 * time.Second)
close(shouldQuit) // close 函数用来关闭通道,表示没有更多的值将被发送到该通道
}
当协程中 time.Sleep() 配置小于 2s,就会执行 select 中的第一个 case,超过 2s 时,就会执行第二个 case 提示超时。

2.3 判断 channel 是否阻塞
判断是否阻塞,实际上就是加上 default,当其他 case 都获取不到值时,通过 default 的操作,来知道 channel 是否阻塞:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 1)
// // 向 channel 发送一个值
// ch <- 1
// fmt.Println("向 channel 发送了一个值:1")
// 使用 select 语句判断 channel 是否阻塞
select {
case v := <-ch:
fmt.Println("从 channel 中读取到值:", v)
default:
fmt.Println("channel 阻塞")
}
fmt.Println("模拟阻塞等待。。")
// 模拟阻塞等待
time.Sleep(2 * time.Second)
fmt.Println("模拟阻塞等待。。2s")
}

三、select 并发编程
select 同时监控多个 channel,直到其中一个 channel 准备好:
package main
import (
"fmt"
"time"
)
func test1(ch chan string) {
time.Sleep(time.Second * 5)
ch <- "test1"
}
func test2(ch chan string) {
time.Sleep(time.Second * 2)
ch <- "test2"
}
func main() {
// 2个管道
output1 := make(chan string)
output2 := make(chan string)
// 跑2个子协程,写数据
go test1(output1)
go test2(output2)
// 用select监控
select {
case s1 := <-output1:
fmt.Println("s1 =", s1)
case s2 := <-output2:
fmt.Println("s2 =", s2)
}
}
如下代码,判断管道是否存满:(设置写入比读取快)
package main
import (
"fmt"
"time"
)
// 判断管道有没有存满
func main() {
// 创建管道
output1 := make(chan string, 2)
// 子协程写数据
go write(output1)
// 取数据
for s := range output1 {
fmt.Println("res:", s)
time.Sleep(time.Second) // 间隔 1s 读取
}
}
func write(ch chan string) {
for {
select {
// 尝试写数据
case ch <- "hello":
fmt.Println("write hello")
default:
fmt.Println("channel full")
}
time.Sleep(time.Millisecond * 500) // 间隔 0.5s 写入
}
}

参考:http://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/select.htm
若想了解 select 底层原理可以参考:https://cloud.tencent.com/developer/article/2205909
select 条件语句【GO 基础】的更多相关文章
- python 条件语句和基础数据类型
条件语句 if 条件: pass else: pass 如果1等于1,输出欢迎进入东京热,否则输出欢迎进入一本道 ==: print("欢迎进入东京热") else: print( ...
- 孤荷凌寒自学python第十四天python代码的书写规范与条件语句及判断条件式
孤荷凌寒自学python第十四天python代码的书写规范与条件语句及判断条件式 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 在我学习过的所有语言中,对VB系的语言比较喜欢,而对C系和J系 ...
- qtp自动化测试-条件语句 if select case
1 if 语句 if condition then end if If condition Then [statements] [ElseIf condition-n Then [else ...
- VBS基础篇 - 条件语句
经常地,当我们编写代码时,我们需要根据不同的判断执行不同操作,我们可以使用条件语句完成这个工作. If...Then...Else 在下面的情况中,您可以使用 If...Then...Else 语句: ...
- JS一周游~(基础、运算符、条件语句)
一.基础篇 JavaScript 基于浏览器(客户端).基于(面向)对象{没有继承}.事件驱动(要有对象).脚本语言(灵活多变) 1.作用 表单的验证,减轻服务端的压力 添加页面动画效果 动态更改页面 ...
- python基础、字符串和if条件语句,while循环,跳出循环、结束循环
一:Python基础 1.文件后缀名: .py 2.Python2中读中文要在文件头写: -*-coding:utf8-*- 3.input用法 n为变量,代指某一变化的值 n = inpu ...
- shell基础(七)-条件语句
条件语句在编写脚本经常遇到:用于处理逻辑问题. 一 IF 语句 if 语句通过关系运算符判断表达式的真假来决定执行哪个分支.Shell 有三种 if ... else 语句: if ... fi 语句 ...
- Python基础 之 变量、用户交互、if条件语句、while循环语句、编码、逻辑运算
一.Python介绍 Python 崇尚优美.清晰.简单 Python是一门动态解释型的强制性定义的语言. 二.编译型和解释型的区别 编译型:一次性将所有与程序编译成二进制文件. 缺点:开发效率低,不 ...
- JAVA基础——Switch条件语句
JAVA基础——switch 条件语句 switch语句结构: switch(表达式){ case值1: 语句体1: break: case值2: 语句体2: break: case值3: 语句体3: ...
- python基础—条件语句
一.Python基础 1.第一句python print('hello,world') Q: 后缀名可以任意? A: 导入模块时,如果不是.py后缀,会出错. 2.两种执行的方式: -python解 ...
随机推荐
- C# 求两个时间的差值
商品保质期 //dateStart:系统时间: dateEnd :物品到期日期 DateTime dateStart = DateTime.Now.Date;//2021/7/8 DateTime d ...
- C#不显示小数点0部分
c#去掉小数点后的无效0 ,保留指定位数的小数,比如10.0显示成10,小数部分会四舍五入 float value = 0.0500f; value.ToString("0.##" ...
- 基于新浪微博海量用户行为数据、博文数据数据分析:包括综合指数、移动指数、PC指数三个指数
基于新浪微博海量用户行为数据.博文数据数据分析:包括综合指数.移动指数.PC指数三个指数 项目介绍 微指数是基于海量用户行为数据.博文数据,采用科学计算方法统计得出的反映不同事件领域发展状况的指数产品 ...
- 强化学习从基础到进阶-常见问题和面试必知必答[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战
强化学习从基础到进阶-常见问题和面试必知必答[3]:表格型方法:Sarsa.Qlearning:蒙特卡洛策略.时序差分等以及Qlearning项目实战 1.核心词汇 概率函数和奖励函数:概率函数定量地 ...
- (C语言)每日代码||2023.12.24||fwrite()可以写入字符数组中的'\0'
void test() { FILE* fp = fopen("test.txt", "w"); if (fp == NULL) { perror(" ...
- 如何使用MBP制作Win启动盘
最近有一个需求,想给家人的一台笔记本安装一套win 10的操作系统,但是我手头上现在没有对应的启动U盘. 由于工作原因,很多年没用win了,工作电脑也都是MBP,根本没有之前使用win时熟悉的Ultr ...
- .NET Core开发实战(第34课:MediatR:轻松实现命令查询职责分离模式(CQRS))--学习笔记(下)
34 | MediatR:轻松实现命令查询职责分离模式(CQRS) 实际上我们在定义我的查询的时候,也可以这样定义,例如我们定义一个 MyOrderQuery,把订单的所有名称都输出出去 namesp ...
- .NET Core开发实战(第14课:自定义配置数据源:低成本实现定制化配置方案)--学习笔记
14 | 自定义配置数据源:低成本实现定制化配置方案 这一节讲解如何定义自己的数据源,来扩展配置框架 扩展步骤 1.实现 IConfigurationSource 2.实现 IConfiguratio ...
- AI抠图神器RMBG下载介绍
RMBG是一款先进的AI抠图工具,和其它同类型软件不同的是,RMBG不需要人工勾勒图形轮廓,可以自动识别图像的前景并去除背景,节省大量时间,效果非常惊艳 最新中文版下载: 百度网盘:https://p ...
- 反悔贪心&模拟费用流
贪心是一种常用的算法,它能够获得局部最优解,但我们往往需要的是全局最优解,所以我们在贪心的时候加入和反悔的机制,让他能够得到全局最优解. 由于网络流中的退流操作本质上也是反悔贪心,所以在实现反悔贪心时 ...