目录
- 什么是select?
- 基本语法
- 使用场景举例
- 1.监听多个 channel 的数据
- 2.实现超时机制
- 3.非阻塞发送或接收(default 分支)
- 4.检测通道是否关闭
- select 的行为特点
- 与 goroutine 配合:生产者/消费者模型
- select + for:常见循环写法
- select 常见陷阱
- 实战建议
- 例子:context 控制退出(推荐生产使用)
- 拓展资料
什么是select?
select
是 Go 中用于多路 channel 操作的控制结构,它可以监听多个 channel 的发送与接收操作,当其中某一个可以进行时就执行对应的语句,从而实现非阻塞并发通信。
基本语法
select case val := <-ch1: // ch1 可读时执行case ch2 <- 100: // ch2 可写时执行default: // 所有 channel 都阻塞时执行(可选)}
-
每个
case
必须是发送(ch <- val)或接收(val := <-ch) -
只会执行一个可操作的
case
(如果多个都可以随机挑一个) -
如果都阻塞,且没有
default
,select
会阻塞等待 -
如果包含
default
,它会立即执行,哪怕其他case
可能之后才可操作
使用场景举例
1.监听多个 channel 的数据
select case msg1 := <-ch1: fmt.Println(“收到 ch1:”, msg1)case msg2 := <-ch2: fmt.Println(“收到 ch2:”, msg2)}
2.实现超时机制
select case msg := <-ch: fmt.Println(“收到:”, msg)case <-time.After(2 * time.Second): fmt.Println(“超时”)}
time.After
返回一个 channel,在指定时刻后变为可读,实现优雅的 timeout。
3.非阻塞发送或接收(default 分支)
select case ch <- data: fmt.Println(“发送成功”)default: fmt.Println(“channel 满,放弃发送”)}
4.检测通道是否关闭
select case v, ok := <-ch: if !ok fmt.Println(“通道关闭”) } else fmt.Println(“收到:”, v) }}
select 的行为特点
行为 | 描述 |
---|---|
随机调度 | 多个 case 同时可用时随机选择一个执行(防止饥饿) |
阻塞等待 | 所有 case 阻塞时,select 自身也阻塞 |
default 分支 | 所有 case 阻塞时立即执行,避免阻塞 |
只选一个 | 同时满足多个时只执行其中一个 |
与 goroutine 配合:生产者/消费者模型
func producer(ch chan int) for i := 0; i < 5; i++ ch <- i } close(ch)}?func consumer(ch chan int, done chan struct}) for select case val, ok := <-ch: if !ok done <- struct}} return } fmt.Println(“消费:”, val) } }}
select + for:常见循环写法
for select case msg := <-ch: fmt.Println(“收到:”, msg) case <-time.After(5 * time.Second): fmt.Println(“超时退出”) return }}
select 常见陷阱
陷阱 | 描述 |
---|---|
忘记 default 导致阻塞 | 如果所有 case 阻塞,select 也阻塞 |
无限阻塞 | select 中所有 channel 永远不会可用 |
channel 已关闭 | 向已关闭通道写入会 panic,应特别小心 |
超时误用 | 使用 time.After() 时,不要在循环里频繁创建新 channel,否则内存泄漏 |
&x1f50d;解决建议:使用time.NewTimer
,并复用 timer。
实战建议
建议 | 缘故 |
---|---|
用 select 实现超时控制 | 比较优雅且非阻塞 |
select + default 实现非阻塞通信 | 避免 goroutine 卡死 |
用 select + context.Done() 控制退出 | 在大型体系中更适合 |
例子:context 控制退出(推荐生产使用)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()?ch := make(chan int)?go func() time.Sleep(2 * time.Second) ch <- 42}()?select case <-ctx.Done(): fmt.Println(“操作取消/超时:”, ctx.Err())case val := <-ch: fmt.Println(“接收到数据:”, val)}
拓展资料
维度 | 说明 |
---|---|
功能 | 实现 channel 的多路复用监听 |
优点 | 非阻塞、高效、优雅处理通信控制 |
结合 | time.After、context、default 最佳组合 |
场景 | goroutine 退出、任务超时、并发协程间通信控制等 |
到此这篇关于Go语言中select使用的文章就介绍到这了,更多相关Go select详解内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
无论兄弟们可能感兴趣的文章:
- Go语言中Select语句用法实例
- golang中的select关键字用法拓展资料
- 简介Go语言中的select语句的用法
- 深入浅出Golang中select的实现原理
- goselect的用法
- Golang关键字select的常用用法拓展资料
- 详解golang开发中select多路选择
- golang中select语句的简单实例