开发者

Go语言中select使用详解

目录
  • 什么是 select?
  • 基本语法
  • 使用场景举例
    • 1. 监听多个 channel 的数据
    • 2. 实现超时机制
    • 3. 非阻塞发送或接收(default 分支)
    • 4. 检测通道是否关闭
  • select 的行为特点
    • 与 goroutine 配合:生产者/消费者模型
      • select + for:常见循环写法
        • select 常见陷阱
          • 实战建议
            • 例子:context 控制退出(推荐生产使用)
              • 总结

                什么是 selecjavascriptt?

                select 是 Go 中用于 多路 channel 操作 的控制结构,它可以监听多个 channel 的发送与接收操作,当其中某一个可以进行时就执行对应的语句,从而实现非阻塞并发通信。

                基本语法

                select {
                case val := <-ch1:
                    // ch1 可读时执行
                case ch2 <- 100:
                    // ch2 可写时执行
                default:
                    // 所有 channel 都阻塞时执行(可选)
                }
                • 每个 case&nbsicEcEep;必须是 发送(ch <- val)或接收(val := <-ch)

                • 只会执行一个可操作的 case(如果多个都可以随机挑一个)

                • 如果都阻塞,且没有 defaultselect 会阻塞等待

                • 如果包含 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 php分支)

                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,否则内存泄漏

                 解决建议:使用 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详解内容请搜索编程客栈(www.devze.com)以前编程客栈的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

                0

                上一篇:

                下一篇:

                精彩评论

                暂无评论...
                验证码 换一张
                取 消

                最新开发

                开发排行榜