Golang Select 深度解析
基于 Go 1.26.4 源码,源码路径:
github.com/go-go1.26.4核心源文件:
runtime/select.go、runtime/chan.go、cmd/compile/internal/walk/select.go
select 是 Go 语言与switch相似但本质不同的控制结构。每一个 case 必须是 channel 的发送或者接收操作,当多个 channel 同时就绪的时候,select 会随机选择其中一个来执行,这种随机性是为了保证公平------没有任何一个 channel 会被饥饿。
1 select 功能完整介绍
1.1 语法与核心概念
Go 的 select 语句是专门为 channel 设计的多路复用器,类似于 Unix 的 select/poll/epoll:
go
select {
case v := <-ch1: // 接收 case
fmt.Println("received:", v)
case ch2 <- value: // 发送 case
fmt.Println("sent:", value)
case v, ok := <-ch3: // 接收 + ok 判断
if !ok { fmt.Println("ch3 closed") }
default: // 非阻塞
fmt.Println("no channel ready")
}
1.2 核心语义规则
| 规则 | 说明 |
|---|---|
| 随机选择 | 多个 case 同时就绪时,随机选择一个执行 |
| 阻塞等待 | 没有 case 就绪且无 default → 阻塞当前 goroutine |
| 非阻塞 | 有 default → 无 case 就绪时执行 default |
| 空 select | select{} → 永久阻塞(等同于死锁) |
| nil channel | nil channel 的 case 永远不会被选中 |
| close 优先 | 已关闭的 channel 的接收 case 立刻就绪 |
#mermaid-svg-B8fbKvo859ByU9QT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-B8fbKvo859ByU9QT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B8fbKvo859ByU9QT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B8fbKvo859ByU9QT .error-icon{fill:#552222;}#mermaid-svg-B8fbKvo859ByU9QT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B8fbKvo859ByU9QT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B8fbKvo859ByU9QT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B8fbKvo859ByU9QT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B8fbKvo859ByU9QT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B8fbKvo859ByU9QT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B8fbKvo859ByU9QT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B8fbKvo859ByU9QT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B8fbKvo859ByU9QT .marker.cross{stroke:#333333;}#mermaid-svg-B8fbKvo859ByU9QT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B8fbKvo859ByU9QT p{margin:0;}#mermaid-svg-B8fbKvo859ByU9QT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B8fbKvo859ByU9QT .cluster-label text{fill:#333;}#mermaid-svg-B8fbKvo859ByU9QT .cluster-label span{color:#333;}#mermaid-svg-B8fbKvo859ByU9QT .cluster-label span p{background-color:transparent;}#mermaid-svg-B8fbKvo859ByU9QT .label text,#mermaid-svg-B8fbKvo859ByU9QT span{fill:#333;color:#333;}#mermaid-svg-B8fbKvo859ByU9QT .node rect,#mermaid-svg-B8fbKvo859ByU9QT .node circle,#mermaid-svg-B8fbKvo859ByU9QT .node ellipse,#mermaid-svg-B8fbKvo859ByU9QT .node polygon,#mermaid-svg-B8fbKvo859ByU9QT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B8fbKvo859ByU9QT .rough-node .label text,#mermaid-svg-B8fbKvo859ByU9QT .node .label text,#mermaid-svg-B8fbKvo859ByU9QT .image-shape .label,#mermaid-svg-B8fbKvo859ByU9QT .icon-shape .label{text-anchor:middle;}#mermaid-svg-B8fbKvo859ByU9QT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B8fbKvo859ByU9QT .rough-node .label,#mermaid-svg-B8fbKvo859ByU9QT .node .label,#mermaid-svg-B8fbKvo859ByU9QT .image-shape .label,#mermaid-svg-B8fbKvo859ByU9QT .icon-shape .label{text-align:center;}#mermaid-svg-B8fbKvo859ByU9QT .node.clickable{cursor:pointer;}#mermaid-svg-B8fbKvo859ByU9QT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B8fbKvo859ByU9QT .arrowheadPath{fill:#333333;}#mermaid-svg-B8fbKvo859ByU9QT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B8fbKvo859ByU9QT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B8fbKvo859ByU9QT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B8fbKvo859ByU9QT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B8fbKvo859ByU9QT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B8fbKvo859ByU9QT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B8fbKvo859ByU9QT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B8fbKvo859ByU9QT .cluster text{fill:#333;}#mermaid-svg-B8fbKvo859ByU9QT .cluster span{color:#333;}#mermaid-svg-B8fbKvo859ByU9QT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-B8fbKvo859ByU9QT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B8fbKvo859ByU9QT rect.text{fill:none;stroke-width:0;}#mermaid-svg-B8fbKvo859ByU9QT .icon-shape,#mermaid-svg-B8fbKvo859ByU9QT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B8fbKvo859ByU9QT .icon-shape p,#mermaid-svg-B8fbKvo859ByU9QT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B8fbKvo859ByU9QT .icon-shape .label rect,#mermaid-svg-B8fbKvo859ByU9QT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B8fbKvo859ByU9QT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B8fbKvo859ByU9QT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B8fbKvo859ByU9QT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 多个就绪
仅一个就绪
无就绪
Yes
No
select 语句
有 case 就绪?
随机选一个执行
pollorder 随机化
执行该 case
有 default?
执行 default
★ 阻塞当前 G
注册到所有 channel
gopark 挂起
某个 channel 就绪
唤醒 G
从其他 channel 注销
执行选中 case
1.3 适用场景
#mermaid-svg-6y5YQsDiuhIygQmu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6y5YQsDiuhIygQmu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6y5YQsDiuhIygQmu .error-icon{fill:#552222;}#mermaid-svg-6y5YQsDiuhIygQmu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6y5YQsDiuhIygQmu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6y5YQsDiuhIygQmu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6y5YQsDiuhIygQmu .marker.cross{stroke:#333333;}#mermaid-svg-6y5YQsDiuhIygQmu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6y5YQsDiuhIygQmu p{margin:0;}#mermaid-svg-6y5YQsDiuhIygQmu .edge{stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .section--1 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section--1 path,#mermaid-svg-6y5YQsDiuhIygQmu .section--1 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section--1 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section--1 text{fill:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth--1{stroke-width:17;}#mermaid-svg-6y5YQsDiuhIygQmu .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-0 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-0 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-0 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-0 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-0 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-0{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-0{stroke-width:14;}#mermaid-svg-6y5YQsDiuhIygQmu .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-1 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-1 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-1 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-1 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-1 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-1{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-1{stroke-width:11;}#mermaid-svg-6y5YQsDiuhIygQmu .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-2 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-2 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-2 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-2 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-2 text{fill:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-2{stroke-width:8;}#mermaid-svg-6y5YQsDiuhIygQmu .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-3 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-3 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-3 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-3 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-3 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-3{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-3{stroke-width:5;}#mermaid-svg-6y5YQsDiuhIygQmu .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-4 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-4 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-4 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-4 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-4 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-4{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-4{stroke-width:2;}#mermaid-svg-6y5YQsDiuhIygQmu .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-5 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-5 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-5 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-5 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-5 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-5{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-5{stroke-width:-1;}#mermaid-svg-6y5YQsDiuhIygQmu .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-6 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-6 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-6 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-6 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-6 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-6{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-6{stroke-width:-4;}#mermaid-svg-6y5YQsDiuhIygQmu .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-7 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-7 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-7 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-7 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-7 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-7{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-7{stroke-width:-7;}#mermaid-svg-6y5YQsDiuhIygQmu .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-8 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-8 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-8 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-8 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-8 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-8{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-8{stroke-width:-10;}#mermaid-svg-6y5YQsDiuhIygQmu .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-9 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-9 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-9 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-9 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-9 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-9{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-9{stroke-width:-13;}#mermaid-svg-6y5YQsDiuhIygQmu .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-10 rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-10 path,#mermaid-svg-6y5YQsDiuhIygQmu .section-10 circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-10 polygon,#mermaid-svg-6y5YQsDiuhIygQmu .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-10 text{fill:black;}#mermaid-svg-6y5YQsDiuhIygQmu .node-icon-10{font-size:40px;color:black;}#mermaid-svg-6y5YQsDiuhIygQmu .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .edge-depth-10{stroke-width:-16;}#mermaid-svg-6y5YQsDiuhIygQmu .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled,#mermaid-svg-6y5YQsDiuhIygQmu .disabled circle,#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:lightgray;}#mermaid-svg-6y5YQsDiuhIygQmu .disabled text{fill:#efefef;}#mermaid-svg-6y5YQsDiuhIygQmu .section-root rect,#mermaid-svg-6y5YQsDiuhIygQmu .section-root path,#mermaid-svg-6y5YQsDiuhIygQmu .section-root circle,#mermaid-svg-6y5YQsDiuhIygQmu .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-6y5YQsDiuhIygQmu .section-root text{fill:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .section-root span{color:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .section-2 span{color:#ffffff;}#mermaid-svg-6y5YQsDiuhIygQmu .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-6y5YQsDiuhIygQmu .edge{fill:none;}#mermaid-svg-6y5YQsDiuhIygQmu .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-6y5YQsDiuhIygQmu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go Select 适用场景
超时控制
select + time.After
context.Done
非阻塞收发
default 分支
尝试性操作
多路复用
监听多个 channel
合并数据流
退出信号
done channel
context cancel
限流
ticker + quit
定时任务
2 编译器对 select 的重写优化
源码位置:cmd/compile/internal/walk/select.go
编译器在编译期对 select 做了优化重写,只有通用情况才调用 selectgo:

2.1 优化1:零 case select → block()
go
// 源码
select {}
// 编译器重写为
runtime.block()
// → gopark(nil, nil, waitReasonSelectNoCases, traceBlockForever, 1)
// 永久阻塞,等价于死锁
2.2 优化2:单 case + 无 default → 直接 channel 操作
go
// 源码
select {
case v := <-ch:
fmt.Println(v)
}
// 编译器重写为:直接调用 chanrecv
v := <-ch
fmt.Println(v)
// 无需 selectgo 的开销
2.3 优化3:单 case + default → 非阻塞操作
go
// 源码
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("no data")
}
// 编译器重写为:调用 selectnbrecv(非阻塞接收)
if selected, ok := selectnbrecv(&v, ch); selected {
fmt.Println("received:", v)
} else {
fmt.Println("no data")
}
// selectnbrecv 内部就是 chanrecv(c, elem, block=false)
func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {
return chanrecv(c, elem, false)
}
go
// 发送 + default 的重写
select {
case ch <- value:
fmt.Println("sent")
default:
fmt.Println("channel full")
}
// 编译器重写为
if selectnbsend(ch, value) {
fmt.Println("sent")
} else {
fmt.Println("channel full")
}
// selectnbsend 内部
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
return chansend(c, elem, false, sys.GetCallerPC())
}
2.4 通用情况:多 case → selectgo
3 个及以上 case(或有 default 的 2 case)编译器调用 selectgo:
go
// 编译器生成的伪代码:
// 1. 分配 scase 数组(栈上)
selv := [ncases]scase{
{c: ch1, elem: &v1}, // case 0: 接收
{c: ch2, elem: &v2}, // case 1: 发送
...
}
// 2. 分配 order 数组(栈上)
order := [2*ncases]uint16{}
// 3. 调用 selectgo
chosen, recvOK := selectgo(&selv[0], &order[0], pc0, nsends, nrecvs, dflt==nil)
// 4. 根据 chosen 分发到对应 case
switch chosen {
case 0: // 第一个 case 被选中
// ...
case 1: // 第二个 case 被选中
// ...
case -1: // default 被选中
// ...
}
#mermaid-svg-cSmCHMuE86puT555{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cSmCHMuE86puT555 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cSmCHMuE86puT555 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cSmCHMuE86puT555 .error-icon{fill:#552222;}#mermaid-svg-cSmCHMuE86puT555 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cSmCHMuE86puT555 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cSmCHMuE86puT555 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cSmCHMuE86puT555 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cSmCHMuE86puT555 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cSmCHMuE86puT555 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cSmCHMuE86puT555 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cSmCHMuE86puT555 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cSmCHMuE86puT555 .marker.cross{stroke:#333333;}#mermaid-svg-cSmCHMuE86puT555 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cSmCHMuE86puT555 p{margin:0;}#mermaid-svg-cSmCHMuE86puT555 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cSmCHMuE86puT555 .cluster-label text{fill:#333;}#mermaid-svg-cSmCHMuE86puT555 .cluster-label span{color:#333;}#mermaid-svg-cSmCHMuE86puT555 .cluster-label span p{background-color:transparent;}#mermaid-svg-cSmCHMuE86puT555 .label text,#mermaid-svg-cSmCHMuE86puT555 span{fill:#333;color:#333;}#mermaid-svg-cSmCHMuE86puT555 .node rect,#mermaid-svg-cSmCHMuE86puT555 .node circle,#mermaid-svg-cSmCHMuE86puT555 .node ellipse,#mermaid-svg-cSmCHMuE86puT555 .node polygon,#mermaid-svg-cSmCHMuE86puT555 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cSmCHMuE86puT555 .rough-node .label text,#mermaid-svg-cSmCHMuE86puT555 .node .label text,#mermaid-svg-cSmCHMuE86puT555 .image-shape .label,#mermaid-svg-cSmCHMuE86puT555 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cSmCHMuE86puT555 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cSmCHMuE86puT555 .rough-node .label,#mermaid-svg-cSmCHMuE86puT555 .node .label,#mermaid-svg-cSmCHMuE86puT555 .image-shape .label,#mermaid-svg-cSmCHMuE86puT555 .icon-shape .label{text-align:center;}#mermaid-svg-cSmCHMuE86puT555 .node.clickable{cursor:pointer;}#mermaid-svg-cSmCHMuE86puT555 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cSmCHMuE86puT555 .arrowheadPath{fill:#333333;}#mermaid-svg-cSmCHMuE86puT555 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cSmCHMuE86puT555 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cSmCHMuE86puT555 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cSmCHMuE86puT555 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cSmCHMuE86puT555 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cSmCHMuE86puT555 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cSmCHMuE86puT555 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cSmCHMuE86puT555 .cluster text{fill:#333;}#mermaid-svg-cSmCHMuE86puT555 .cluster span{color:#333;}#mermaid-svg-cSmCHMuE86puT555 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cSmCHMuE86puT555 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cSmCHMuE86puT555 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cSmCHMuE86puT555 .icon-shape,#mermaid-svg-cSmCHMuE86puT555 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cSmCHMuE86puT555 .icon-shape p,#mermaid-svg-cSmCHMuE86puT555 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cSmCHMuE86puT555 .icon-shape .label rect,#mermaid-svg-cSmCHMuE86puT555 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cSmCHMuE86puT555 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cSmCHMuE86puT555 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cSmCHMuE86puT555 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0
1 + 无 default
1 + default
≥2 或 2+default
select 语句
case 数量?
block() 永久阻塞
直接 channel 操作
chansend/chanrecv
selectnbsend/selectnbrecv
非阻塞操作
selectgo 通用路径
3 selectgo 完整源码逐行解析
源码位置:runtime/select.go:116
3.1 scase 结构体
go
type scase struct {
c *hchan // ★ 关联的 channel
elem unsafe.Pointer // ★ 数据元素指针
// 发送 case: 指向待发送的值
// 接收 case: 指向接收变量的地址
}
scase 内存布局 (16 bytes on 64-bit)
┌─────────────────────────────────┐
│ c *hchan (8B) │ → channel 指针
├─────────────────────────────────┤
│ elem unsafe.P (8B) │ → 数据指针
└─────────────────────────────────┘
编译器分配的 scase 数组(栈上):
case 0: recv → scase{c: ch1, elem: &v}
case 1: send → scase{c: ch2, elem: &val}
case 2: recv → scase{c: ch3, elem: nil} // v, ok:= <-ch3 的 ok 分支
default: 不在 scase 数组中(dflt 单独处理)
3.2 selectgo 参数与返回值
go
func selectgo(
cas0 *scase, // scase 数组首地址
order0 *uint16, // order 数组首地址(2*ncases 大小)
pc0 *uintptr, // race detector PC 数组
nsends int, // 发送 case 数量
nrecvs int, // 接收 case 数量
block bool, // 是否阻塞(无 default 时 block=true)
) (int, bool) // 返回: (选中的 case 索引, 接收是否成功)
关键设计:case 编排顺序
scase 数组布局:
[0] [1] ... [nsends-1] [nsends] ... [ncases-1]
send0 send1 ... send_last recv0 ... recv_last
↑─────── nsends ──────────↑↑──────── nrecvs ───────────────↑
发送 case 排在前面,接收 case 排在后面
这样 casi < nsends 表示是发送 case,否则是接收 case
3.3 Phase 0: 初始化与 pollorder 随机化

现在我们进入了运行时的核心。selectgo 函数首先做初始化工作。它从输入的 scase 数组中创建两个辅助数组------pollorder 和 lockorder。pollorder 是通过 fastrandn 随机打乱的,决定了下一次遍历 case 的顺序,这样保证了公平性,没有任何 channel 会被饿死。lockorder 则是按照 channel 的地址进行排序的,这样就保证了多个 goroutine 同时对同一批 channel 加锁的时候锁定顺序是一致的,从根本上避免了死锁的可能。这里有一个很重要的细节,你可以看到代码中使用了一个 for 循环配合 fastrandn 来做 Fisher-Yates 洗牌算法,每次循环随机交换两个元素的位置。初始化完毕后,sel lock 会按 lockorder 的顺序依次锁定所有 channel,然后程序进入主循环。
go
func selectgo(...) (int, bool) {
// 解析参数
ncases := nsends + nrecvs
scases := cas1[:ncases:ncases]
pollorder := order1[:ncases:ncases] // 前半段: 轮询顺序
lockorder := order1[ncases:][:ncases:ncases] // 后半段: 加锁顺序
// ★★★ 生成随机轮询顺序(Fisher-Yates 洗牌算法)★★★
norder := 0
for i := range scases {
cas := &scases[i]
if cas.c == nil {
cas.elem = nil // nil channel → 跳过
continue // 不加入 pollorder 和 lockorder
}
// ★ 随机插入位置 j
j := cheaprandn(uint32(norder + 1))
pollorder[norder] = pollorder[j] // 移动 j 位置的值到末尾
pollorder[j] = uint16(i) // 新值插入到 j 位置
norder++
}
pollorder = pollorder[:norder]
// 结果: pollorder 是 [0,1,2,...,norder-1] 的随机排列
// 例: ncases=4, pollorder 可能是 [2, 0, 3, 1]
Fisher-Yates 洗牌过程图示:
初始: pollorder = []
i=0: j=random(1)=0 → pollorder = [0]
i=1: j=random(2)=1 → pollorder = [0, 1]
i=2: j=random(3)=0 → pollorder = [2, 1, 0] ← 0 被移到末尾,2 插入位置 0
i=3: j=random(4)=2 → pollorder = [2, 1, 3, 0] ← 0 被移到末尾,3 插入位置 2
最终轮询顺序: 2 → 1 → 3 → 0
★ 这保证了多个 case 同时就绪时,被选中的概率均等
3.4 Phase 0 续: lockorder 排序
go
// ★★★ 按 channel 地址排序 lockorder(堆排序)★★★
// 原因: 防止死锁!
// 如果两个 G 同时 select 多个 channel,
// 加锁顺序不一致可能导致死锁
// 按地址排序保证了所有 G 的加锁顺序一致
// 堆排序建立最大堆
for i := range lockorder {
j := i
c := scases[pollorder[i]].c
for j > 0 && scases[lockorder[(j-1)/2]].c.sortkey() < c.sortkey() {
k := (j - 1) / 2
lockorder[j] = lockorder[k]
j = k
}
lockorder[j] = pollorder[i]
}
// 堆排序提取
for i := len(lockorder) - 1; i >= 0; i-- {
// ... 标准堆排序 ...
}
// 结果: lockorder 按 channel 地址从小到大排列
#mermaid-svg-b9dgK2DTB8TGXLAC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b9dgK2DTB8TGXLAC .error-icon{fill:#552222;}#mermaid-svg-b9dgK2DTB8TGXLAC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b9dgK2DTB8TGXLAC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b9dgK2DTB8TGXLAC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b9dgK2DTB8TGXLAC .marker.cross{stroke:#333333;}#mermaid-svg-b9dgK2DTB8TGXLAC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b9dgK2DTB8TGXLAC p{margin:0;}#mermaid-svg-b9dgK2DTB8TGXLAC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster-label text{fill:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster-label span{color:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster-label span p{background-color:transparent;}#mermaid-svg-b9dgK2DTB8TGXLAC .label text,#mermaid-svg-b9dgK2DTB8TGXLAC span{fill:#333;color:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC .node rect,#mermaid-svg-b9dgK2DTB8TGXLAC .node circle,#mermaid-svg-b9dgK2DTB8TGXLAC .node ellipse,#mermaid-svg-b9dgK2DTB8TGXLAC .node polygon,#mermaid-svg-b9dgK2DTB8TGXLAC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b9dgK2DTB8TGXLAC .rough-node .label text,#mermaid-svg-b9dgK2DTB8TGXLAC .node .label text,#mermaid-svg-b9dgK2DTB8TGXLAC .image-shape .label,#mermaid-svg-b9dgK2DTB8TGXLAC .icon-shape .label{text-anchor:middle;}#mermaid-svg-b9dgK2DTB8TGXLAC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b9dgK2DTB8TGXLAC .rough-node .label,#mermaid-svg-b9dgK2DTB8TGXLAC .node .label,#mermaid-svg-b9dgK2DTB8TGXLAC .image-shape .label,#mermaid-svg-b9dgK2DTB8TGXLAC .icon-shape .label{text-align:center;}#mermaid-svg-b9dgK2DTB8TGXLAC .node.clickable{cursor:pointer;}#mermaid-svg-b9dgK2DTB8TGXLAC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b9dgK2DTB8TGXLAC .arrowheadPath{fill:#333333;}#mermaid-svg-b9dgK2DTB8TGXLAC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b9dgK2DTB8TGXLAC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b9dgK2DTB8TGXLAC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b9dgK2DTB8TGXLAC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b9dgK2DTB8TGXLAC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b9dgK2DTB8TGXLAC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster text{fill:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC .cluster span{color:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-b9dgK2DTB8TGXLAC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b9dgK2DTB8TGXLAC rect.text{fill:none;stroke-width:0;}#mermaid-svg-b9dgK2DTB8TGXLAC .icon-shape,#mermaid-svg-b9dgK2DTB8TGXLAC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b9dgK2DTB8TGXLAC .icon-shape p,#mermaid-svg-b9dgK2DTB8TGXLAC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b9dgK2DTB8TGXLAC .icon-shape .label rect,#mermaid-svg-b9dgK2DTB8TGXLAC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b9dgK2DTB8TGXLAC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b9dgK2DTB8TGXLAC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b9dgK2DTB8TGXLAC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} lockorder (排序)
0 → 2 → 1 → 3
ch0.addr < ch2.addr < ch1.addr < ch3.addr
决定加锁顺序
pollorder (随机)
2 → 1 → 3 → 0
决定检查顺序
为什么需要两种顺序?
| 顺序 | 作用 | 生成方式 |
|---|---|---|
| pollorder | 决定检查 case 的顺序 → 保证公平性 | 随机洗牌 |
| lockorder | 决定加锁 channel 的顺序 → 防止死锁 | 按地址排序 |
3.5 sellock:按 lockorder 依次加锁
go
func sellock(scases []scase, lockorder []uint16) {
var c *hchan
for _, o := range lockorder {
c0 := scases[o].c
if c0 != c { // ★ 同一个 channel 不重复加锁
c = c0
lock(&c.lock)
}
}
}
// ★★★ 防死锁原理 ★★★
// 假设:
// G1: select { case ch1: ... case ch2: ... }
// G2: select { case ch2: ... case ch1: ... }
//
// 不排序:
// G1 先锁 ch1, G2 先锁 ch2 → 死锁!
//
// lockorder 排序后(ch1.addr < ch2.addr):
// G1 和 G2 都先锁 ch1, 再锁 ch2 → 不会死锁 ✅

主循环是 select 的核心,分为三个阶段。
- 第一阶段,按照 pollorder 的顺序遍历所有 case,检查每个 channel 的状态。对于接收操作,如果发送等待队列里有 goroutine 在等待,就跳到 recv 标签;如果缓冲区有数据,就跳到 bufrecv 标签;如果 channel 已经关闭了,就跳到 rclose 标签。对于发送操作,如果 channel 已经关闭,直接跳到 sclose 标签会触发 panic;如果接收等待队列里有 goroutine 在等待,就跳到 send 标签;如果缓冲区有空位,就跳到 bufsend 标签。如果没有任何 case 就绪,就进入第二阶段。
- 第二阶段,为每一个 case 创建一个 sudog 结构体,这个结构体代表了当前 goroutine 的等待状态。sudog 会被加入到对应 channel 的发送或者接收等待队列中,所有的 sudog 会串成一个链表挂在当前 goroutine 的 waiting 字段上。然后调用 gopark 把当前 goroutine 挂起。
- 第三阶段,当 goroutine 被唤醒的时候,它从 gp.param 中取回 sudog,然后遍历 lockorder 找到匹配的 case,清理其他 channel 上未使用的 sudog,释放不再需要的结构体,最后跳转到对应的操作标签执行数据的收发。
3.6 Pass 1: 按随机顺序检查是否有 case 就绪
go
// ★★★ Pass 1: 按 pollorder 检查 ★★★
for _, casei := range pollorder {
casi = int(casei)
cas = &scases[casi]
c = cas.c
if casi >= nsends {
// ──── 接收 case ────
// 检查1: 有等待的发送者?
sg = c.sendq.dequeue()
if sg != nil { goto recv }
// 检查2: 缓冲区有数据?
if c.qcount > 0 { goto bufrecv }
// 检查3: 通道已关闭?
if c.closed != 0 { goto rclose }
} else {
// ──── 发送 case ────
// 检查1: 通道已关闭?(发送到已关闭通道 → panic)
if c.closed != 0 { goto sclose }
// 检查2: 有等待的接收者?
sg = c.recvq.dequeue()
if sg != nil { goto send }
// 检查3: 缓冲区有空间?
if c.qcount < c.dataqsiz { goto bufsend }
}
}
// Pass 1 没有找到就绪的 case
if !block {
selunlock(scases, lockorder)
casi = -1
goto retc // 有 default → 返回 -1
}
#mermaid-svg-GV53xh7yXmIpXDMO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GV53xh7yXmIpXDMO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GV53xh7yXmIpXDMO .error-icon{fill:#552222;}#mermaid-svg-GV53xh7yXmIpXDMO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GV53xh7yXmIpXDMO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GV53xh7yXmIpXDMO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GV53xh7yXmIpXDMO .marker.cross{stroke:#333333;}#mermaid-svg-GV53xh7yXmIpXDMO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GV53xh7yXmIpXDMO p{margin:0;}#mermaid-svg-GV53xh7yXmIpXDMO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GV53xh7yXmIpXDMO .cluster-label text{fill:#333;}#mermaid-svg-GV53xh7yXmIpXDMO .cluster-label span{color:#333;}#mermaid-svg-GV53xh7yXmIpXDMO .cluster-label span p{background-color:transparent;}#mermaid-svg-GV53xh7yXmIpXDMO .label text,#mermaid-svg-GV53xh7yXmIpXDMO span{fill:#333;color:#333;}#mermaid-svg-GV53xh7yXmIpXDMO .node rect,#mermaid-svg-GV53xh7yXmIpXDMO .node circle,#mermaid-svg-GV53xh7yXmIpXDMO .node ellipse,#mermaid-svg-GV53xh7yXmIpXDMO .node polygon,#mermaid-svg-GV53xh7yXmIpXDMO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GV53xh7yXmIpXDMO .rough-node .label text,#mermaid-svg-GV53xh7yXmIpXDMO .node .label text,#mermaid-svg-GV53xh7yXmIpXDMO .image-shape .label,#mermaid-svg-GV53xh7yXmIpXDMO .icon-shape .label{text-anchor:middle;}#mermaid-svg-GV53xh7yXmIpXDMO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GV53xh7yXmIpXDMO .rough-node .label,#mermaid-svg-GV53xh7yXmIpXDMO .node .label,#mermaid-svg-GV53xh7yXmIpXDMO .image-shape .label,#mermaid-svg-GV53xh7yXmIpXDMO .icon-shape .label{text-align:center;}#mermaid-svg-GV53xh7yXmIpXDMO .node.clickable{cursor:pointer;}#mermaid-svg-GV53xh7yXmIpXDMO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GV53xh7yXmIpXDMO .arrowheadPath{fill:#333333;}#mermaid-svg-GV53xh7yXmIpXDMO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GV53xh7yXmIpXDMO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GV53xh7yXmIpXDMO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GV53xh7yXmIpXDMO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GV53xh7yXmIpXDMO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GV53xh7yXmIpXDMO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GV53xh7yXmIpXDMO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GV53xh7yXmIpXDMO .cluster text{fill:#333;}#mermaid-svg-GV53xh7yXmIpXDMO .cluster span{color:#333;}#mermaid-svg-GV53xh7yXmIpXDMO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GV53xh7yXmIpXDMO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GV53xh7yXmIpXDMO rect.text{fill:none;stroke-width:0;}#mermaid-svg-GV53xh7yXmIpXDMO .icon-shape,#mermaid-svg-GV53xh7yXmIpXDMO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GV53xh7yXmIpXDMO .icon-shape p,#mermaid-svg-GV53xh7yXmIpXDMO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GV53xh7yXmIpXDMO .icon-shape .label rect,#mermaid-svg-GV53xh7yXmIpXDMO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GV53xh7yXmIpXDMO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GV53xh7yXmIpXDMO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GV53xh7yXmIpXDMO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 接收 case
Yes
No
Yes
No
Yes
No
发送 case
Yes
No
Yes
No
Yes
No
Pass 1: 按 pollorder 检查每个 case
casi >= nsends?
(接收 vs 发送)
sendq 有等待者?
goto recv
直接从发送者接收
qcount > 0?
goto bufrecv
从缓冲区接收
closed != 0?
goto rclose
通道已关闭
此 case 未就绪
closed != 0?
goto sclose
panic!
recvq 有等待者?
goto send
直接发给接收者
qcount < dataqsiz?
goto bufsend
写入缓冲区
检查下一个 case
3.7 Pass 2: 注册到所有 channel 的等待队列
go
// ★★★ Pass 2: 没有 case 就绪 + 需要阻塞 → 注册等待 ★★★
nextp = &gp.waiting
for _, casei := range lockorder { // ★ 按 lockorder(非 pollorder)遍历
casi = int(casei)
cas = &scases[casi]
c = cas.c
sg := acquireSudog() // 从池中获取 sudog
sg.g = gp
sg.isSelect = true // ★ 标记来自 select
sg.elem.set(cas.elem)
sg.c.set(c)
// 构建 gp.waiting 链表(按 lockorder 排列)
*nextp = sg
nextp = &sg.waitlink
if casi < nsends {
c.sendq.enqueue(sg) // 发送 case → 加入 sendq
} else {
c.recvq.enqueue(sg) // 接收 case → 加入 recvq
}
}
// ★ 挂起当前 goroutine
gp.parkingOnChan.Store(true)
gopark(selparkcommit, nil, waitReason, traceBlockSelect, 1)
// ★★★ 此时当前 G 完全停止,等待某个 channel 操作唤醒它 ★★★
selparkcommit------挂起前的解锁:
go
func selparkcommit(gp *g, _ unsafe.Pointer) bool {
gp.activeStackChans = true
gp.parkingOnChan.Store(false)
// ★★★ 在挂起前释放所有 channel 锁 ★★★
// 必须在 gopark 内部解锁,否则:
// 1. 其他 G 无法操作这些 channel
// 2. 可能死锁(持有锁 + 挂起)
var lastc *hchan
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
if sg.c.get() != lastc && lastc != nil {
unlock(&lastc.lock) // 按链表顺序解锁
}
lastc = sg.c.get()
}
if lastc != nil {
unlock(&lastc.lock)
}
return true
}
3.8 被唤醒后的 Pass 3: 清理未成功的注册
go
// ★ 被某个 channel 操作唤醒 ★
gp.activeStackChans = false
// ★★★ 重新加锁所有 channel ★★★
sellock(scases, lockorder)
// 找到唤醒我们的那个 sudog
sg = (*sudog)(gp.param)
// ★★★ Pass 3: 从所有其他 channel 的等待队列中注销 ★★★
casi = -1
cas = nil
caseSuccess = false
sglist = gp.waiting
// 先清空所有 elem(帮助 GC)
for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
sg1.isSelect = false
sg1.elem.set(nil)
sg1.c.set(nil)
}
gp.waiting = nil
// ★ 遍历所有注册的 sudog
for _, casei := range lockorder {
k = &scases[casei]
if sg == sglist {
// ★ 这就是唤醒我们的那个 sudog → 记录选中 case
casi = int(casei)
cas = k
caseSuccess = sglist.success
} else {
// ★ 不是唤醒我们的 → 从等待队列中移除
c = k.c
if int(casei) < nsends {
c.sendq.dequeueSudoG(sglist)
} else {
c.recvq.dequeueSudoG(sglist)
}
}
sgnext = sglist.waitlink
sglist.waitlink = nil
releaseSudog(sglist) // 归还到池
sglist = sgnext
}
其他 Goroutine Channel 2 Channel 1 当前 Goroutine 其他 Goroutine Channel 2 Channel 1 当前 Goroutine #mermaid-svg-1Gcf2Q6x0hi5vrft{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1Gcf2Q6x0hi5vrft .error-icon{fill:#552222;}#mermaid-svg-1Gcf2Q6x0hi5vrft .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1Gcf2Q6x0hi5vrft .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1Gcf2Q6x0hi5vrft .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1Gcf2Q6x0hi5vrft .marker.cross{stroke:#333333;}#mermaid-svg-1Gcf2Q6x0hi5vrft svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1Gcf2Q6x0hi5vrft p{margin:0;}#mermaid-svg-1Gcf2Q6x0hi5vrft .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1Gcf2Q6x0hi5vrft text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1Gcf2Q6x0hi5vrft .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-1Gcf2Q6x0hi5vrft .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-1Gcf2Q6x0hi5vrft #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-1Gcf2Q6x0hi5vrft .sequenceNumber{fill:white;}#mermaid-svg-1Gcf2Q6x0hi5vrft #sequencenumber{fill:#333;}#mermaid-svg-1Gcf2Q6x0hi5vrft #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-1Gcf2Q6x0hi5vrft .messageText{fill:#333;stroke:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1Gcf2Q6x0hi5vrft .labelText,#mermaid-svg-1Gcf2Q6x0hi5vrft .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .loopText,#mermaid-svg-1Gcf2Q6x0hi5vrft .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1Gcf2Q6x0hi5vrft .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-1Gcf2Q6x0hi5vrft .noteText,#mermaid-svg-1Gcf2Q6x0hi5vrft .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-1Gcf2Q6x0hi5vrft .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1Gcf2Q6x0hi5vrft .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1Gcf2Q6x0hi5vrft .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1Gcf2Q6x0hi5vrft .actorPopupMenu{position:absolute;}#mermaid-svg-1Gcf2Q6x0hi5vrft .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-1Gcf2Q6x0hi5vrft .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1Gcf2Q6x0hi5vrft .actor-man circle,#mermaid-svg-1Gcf2Q6x0hi5vrft line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-1Gcf2Q6x0hi5vrft :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Pass 1: 检查就绪 → 无注册 sudog 到 recvq注册 sudog 到 sendqgopark 挂起向 Ch1 发送数据唤醒(通过 send 直接拷贝)被唤醒,重新加锁Pass 3: 找到唤醒者(Ch1)从 sendq 移除 sudog执行 Ch1 对应的 case
3.9 就绪 case 的具体执行路径
recv------从等待的发送者直接接收:
go
recv:
// 可以从沉睡的发送者接收(同步接收)
recv(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)
recvOK = true
goto retc
// recv 内部:
// 无缓冲通道: recvDirect → 直接从发送者栈拷贝到接收者
// 有缓冲通道: 从缓冲区头部取 + 发送者数据放入尾部
// 然后 goready 唤醒发送者
bufrecv------从缓冲区接收:
go
bufrecv:
recvOK = true
qp = chanbuf(c, c.recvx) // 缓冲区当前读取位置
if cas.elem != nil {
typedmemmove(c.elemtype, cas.elem, qp) // 拷贝到接收变量
}
typedmemclr(c.elemtype, qp) // 清空缓冲区 slot
c.recvx++
if c.recvx == c.dataqsiz { c.recvx = 0 } // 环形回绕
c.qcount--
selunlock(scases, lockorder)
goto retc
send------直接发给等待的接收者:
go
send:
send(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)
goto retc
// send 内部:
// sendDirect → 直接从发送者栈拷贝到接收者栈
// goready 唤醒接收者
bufsend------写入缓冲区:
go
bufsend:
typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem) // 写入缓冲区
c.sendx++
if c.sendx == c.dataqsiz { c.sendx = 0 }
c.qcount++
selunlock(scases, lockorder)
goto retc
rclose------从已关闭通道接收:
go
rclose:
selunlock(scases, lockorder)
recvOK = false // ★ received=false
if cas.elem != nil {
typedmemclr(c.elemtype, cas.elem) // 零值
}
goto retc
sclose------向已关闭通道发送:
go
sclose:
selunlock(scases, lockorder)
panic(plainError("send on closed channel")) // ★ panic!
4 selectgo 完整流程总图
#mermaid-svg-VrrinswLxp2nRObF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VrrinswLxp2nRObF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VrrinswLxp2nRObF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VrrinswLxp2nRObF .error-icon{fill:#552222;}#mermaid-svg-VrrinswLxp2nRObF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VrrinswLxp2nRObF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VrrinswLxp2nRObF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VrrinswLxp2nRObF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VrrinswLxp2nRObF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VrrinswLxp2nRObF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VrrinswLxp2nRObF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VrrinswLxp2nRObF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VrrinswLxp2nRObF .marker.cross{stroke:#333333;}#mermaid-svg-VrrinswLxp2nRObF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VrrinswLxp2nRObF p{margin:0;}#mermaid-svg-VrrinswLxp2nRObF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VrrinswLxp2nRObF .cluster-label text{fill:#333;}#mermaid-svg-VrrinswLxp2nRObF .cluster-label span{color:#333;}#mermaid-svg-VrrinswLxp2nRObF .cluster-label span p{background-color:transparent;}#mermaid-svg-VrrinswLxp2nRObF .label text,#mermaid-svg-VrrinswLxp2nRObF span{fill:#333;color:#333;}#mermaid-svg-VrrinswLxp2nRObF .node rect,#mermaid-svg-VrrinswLxp2nRObF .node circle,#mermaid-svg-VrrinswLxp2nRObF .node ellipse,#mermaid-svg-VrrinswLxp2nRObF .node polygon,#mermaid-svg-VrrinswLxp2nRObF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VrrinswLxp2nRObF .rough-node .label text,#mermaid-svg-VrrinswLxp2nRObF .node .label text,#mermaid-svg-VrrinswLxp2nRObF .image-shape .label,#mermaid-svg-VrrinswLxp2nRObF .icon-shape .label{text-anchor:middle;}#mermaid-svg-VrrinswLxp2nRObF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VrrinswLxp2nRObF .rough-node .label,#mermaid-svg-VrrinswLxp2nRObF .node .label,#mermaid-svg-VrrinswLxp2nRObF .image-shape .label,#mermaid-svg-VrrinswLxp2nRObF .icon-shape .label{text-align:center;}#mermaid-svg-VrrinswLxp2nRObF .node.clickable{cursor:pointer;}#mermaid-svg-VrrinswLxp2nRObF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VrrinswLxp2nRObF .arrowheadPath{fill:#333333;}#mermaid-svg-VrrinswLxp2nRObF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VrrinswLxp2nRObF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VrrinswLxp2nRObF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VrrinswLxp2nRObF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VrrinswLxp2nRObF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VrrinswLxp2nRObF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VrrinswLxp2nRObF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VrrinswLxp2nRObF .cluster text{fill:#333;}#mermaid-svg-VrrinswLxp2nRObF .cluster span{color:#333;}#mermaid-svg-VrrinswLxp2nRObF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VrrinswLxp2nRObF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VrrinswLxp2nRObF rect.text{fill:none;stroke-width:0;}#mermaid-svg-VrrinswLxp2nRObF .icon-shape,#mermaid-svg-VrrinswLxp2nRObF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VrrinswLxp2nRObF .icon-shape p,#mermaid-svg-VrrinswLxp2nRObF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VrrinswLxp2nRObF .icon-shape .label rect,#mermaid-svg-VrrinswLxp2nRObF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VrrinswLxp2nRObF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VrrinswLxp2nRObF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VrrinswLxp2nRObF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
false
true
selectgo 入口
Phase 0: 初始化
pollorder 随机洗牌
Fisher-Yates
lockorder 按 ch 地址排序
堆排序
sellock 按 lockorder 加锁
Pass 1: 按 pollorder 检查就绪
有 case 就绪?
执行就绪 case
解锁返回
block=true?
(无 default)
返回 casi=-1
(default)
Pass 2: 注册等待
为每个 case 创建 sudog
加入 channel 等待队列
gopark 挂起
selparkcommit 解锁
被唤醒
sellock 重新加锁
Pass 3: 清理注销
找到唤醒者 case
从其他 channel 移除 sudog
releaseSudog 归还
selunlock 解锁
返回 (casi, recvOK)
5 nil channel 在 select 中的行为
5.1 nil channel 永远不被选中
go
var ch1 chan int // nil
ch2 := make(chan int, 1)
select {
case v := <-ch1: // ★ 永远不会被选中!
fmt.Println(v)
case v := <-ch2:
fmt.Println(v) // 只会走这个
}
源码原因 :cas.c == nil 的 case 被跳过,不加入 pollorder 和 lockorder:
go
for i := range scases {
cas := &scases[i]
if cas.c == nil {
cas.elem = nil // allow GC
continue // ★ 跳过!不加入任何顺序数组
}
// ... 加入 pollorder ...
}
5.2 利用 nil channel 禁用 select 分支
go
func merge(ch1, ch2 <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for ch1 != nil || ch2 != nil {
select {
case v, ok := <-ch1:
if !ok { ch1 = nil; continue } // ★ 设为 nil 禁用此分支
out <- v
case v, ok := <-ch2:
if !ok { ch2 = nil; continue } // ★ 设为 nil 禁用此分支
out <- v
}
}
}()
return out
}
#mermaid-svg-YV1IHhpolXVVEEDA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YV1IHhpolXVVEEDA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YV1IHhpolXVVEEDA .error-icon{fill:#552222;}#mermaid-svg-YV1IHhpolXVVEEDA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YV1IHhpolXVVEEDA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YV1IHhpolXVVEEDA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YV1IHhpolXVVEEDA .marker.cross{stroke:#333333;}#mermaid-svg-YV1IHhpolXVVEEDA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YV1IHhpolXVVEEDA p{margin:0;}#mermaid-svg-YV1IHhpolXVVEEDA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YV1IHhpolXVVEEDA .cluster-label text{fill:#333;}#mermaid-svg-YV1IHhpolXVVEEDA .cluster-label span{color:#333;}#mermaid-svg-YV1IHhpolXVVEEDA .cluster-label span p{background-color:transparent;}#mermaid-svg-YV1IHhpolXVVEEDA .label text,#mermaid-svg-YV1IHhpolXVVEEDA span{fill:#333;color:#333;}#mermaid-svg-YV1IHhpolXVVEEDA .node rect,#mermaid-svg-YV1IHhpolXVVEEDA .node circle,#mermaid-svg-YV1IHhpolXVVEEDA .node ellipse,#mermaid-svg-YV1IHhpolXVVEEDA .node polygon,#mermaid-svg-YV1IHhpolXVVEEDA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YV1IHhpolXVVEEDA .rough-node .label text,#mermaid-svg-YV1IHhpolXVVEEDA .node .label text,#mermaid-svg-YV1IHhpolXVVEEDA .image-shape .label,#mermaid-svg-YV1IHhpolXVVEEDA .icon-shape .label{text-anchor:middle;}#mermaid-svg-YV1IHhpolXVVEEDA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YV1IHhpolXVVEEDA .rough-node .label,#mermaid-svg-YV1IHhpolXVVEEDA .node .label,#mermaid-svg-YV1IHhpolXVVEEDA .image-shape .label,#mermaid-svg-YV1IHhpolXVVEEDA .icon-shape .label{text-align:center;}#mermaid-svg-YV1IHhpolXVVEEDA .node.clickable{cursor:pointer;}#mermaid-svg-YV1IHhpolXVVEEDA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YV1IHhpolXVVEEDA .arrowheadPath{fill:#333333;}#mermaid-svg-YV1IHhpolXVVEEDA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YV1IHhpolXVVEEDA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YV1IHhpolXVVEEDA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YV1IHhpolXVVEEDA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YV1IHhpolXVVEEDA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YV1IHhpolXVVEEDA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YV1IHhpolXVVEEDA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YV1IHhpolXVVEEDA .cluster text{fill:#333;}#mermaid-svg-YV1IHhpolXVVEEDA .cluster span{color:#333;}#mermaid-svg-YV1IHhpolXVVEEDA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YV1IHhpolXVVEEDA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YV1IHhpolXVVEEDA rect.text{fill:none;stroke-width:0;}#mermaid-svg-YV1IHhpolXVVEEDA .icon-shape,#mermaid-svg-YV1IHhpolXVVEEDA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YV1IHhpolXVVEEDA .icon-shape p,#mermaid-svg-YV1IHhpolXVVEEDA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YV1IHhpolXVVEEDA .icon-shape .label rect,#mermaid-svg-YV1IHhpolXVVEEDA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YV1IHhpolXVVEEDA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YV1IHhpolXVVEEDA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YV1IHhpolXVVEEDA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 都关闭后
ch1 关闭后
ch1 关闭前
ch1!=nil → 可选中
ch2!=nil → 可选中
ch1=nil → 永不选中
ch2!=nil → 唯一选项
ch1=nil → 退出
ch2=nil → 退出
6 select 的公平性保证
6.1 pollorder 随机化的必要性
go
// 没有 pollorder 随机化时的问题:
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch1 <- 1
ch2 <- 2
select {
case v := <-ch1: // 总是先检查 ch1
fmt.Println(v) // 永远输出 1,ch2 饥饿!
case v := <-ch2:
fmt.Println(v) // 永远不会执行
}
// 有 pollorder 随机化:
// 每次执行 select 时,检查顺序随机
// ch1 和 ch2 被选中的概率均等
6.2 isSelect 标志与 CAS 竞争
go
// waitq.dequeue 中的特殊处理:
func (q *waitq) dequeue() *sudog {
for {
sgp := q.first
// ...
if sgp.isSelect {
// ★ 来自 select 的 sudog 可能在多个 channel 的等待队列中
// 使用 CAS 确保只有一个 channel 能唤醒它
if !sgp.g.selectDone.CompareAndSwap(0, 1) {
// 另一个 channel 已经赢得了竞争
continue // 跳过,尝试下一个
}
}
return sgp
}
}
场景:
一个 G 在 select 中同时注册到 ch1.recvq 和 ch2.recvq
如果 ch1 和 ch2 同时有数据可读:
1. ch1 的发送者调用 send() → dequeue ch1.recvq 中的 sudog
→ selectDone.CAS(0,1) 成功 → 唤醒 G
2. ch2 的发送者调用 send() → dequeue ch2.recvq 中的 sudog
→ selectDone.CAS(0,1) 失败(已经是1)→ 跳过
→ 继续找下一个等待者
★ CAS 确保一个 G 只被一个 channel 唤醒
7 完整使用示例合集
7.1 超时控制
go
func doWithTimeout(ch <-chan int, timeout time.Duration) (int, error) {
select {
case v := <-ch:
return v, nil
case <-time.After(timeout):
return 0, fmt.Errorf("timeout after %v", timeout)
}
}
7.2 非阻塞发送/接收
go
// 非阻塞接收
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("no data available")
}
// 非阻塞发送
select {
case ch <- value:
fmt.Println("sent")
default:
fmt.Println("channel full, dropped")
}
7.3 优雅退出(done channel 模式)
go
func worker(done <-chan struct{}, jobs <-chan int) {
for {
select {
case <-done:
fmt.Println("worker exiting")
return
case j, ok := <-jobs:
if !ok {
return // jobs channel 已关闭
}
process(j)
}
}
}
7.4 定时器 + 退出
go
func tickerDemo(done <-chan struct{}) {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("tick at", t)
}
}
}
7.5 扇入(多 channel 合并)
go
func fanIn(ch1, ch2 <-chan string) <-chan string {
merged := make(chan string)
go func() {
defer close(merged)
for ch1 != nil || ch2 != nil {
select {
case s, ok := <-ch1:
if !ok { ch1 = nil; continue }
merged <- s
case s, ok := <-ch2:
if !ok { ch2 = nil; continue }
merged <- s
}
}
}()
return merged
}
8 经典问题与优化
8.1 select 中 for range 的空循环问题
go
// ❌ 错误:空 select 导致 CPU 100%
for {
select {
case v := <-ch:
process(v)
default:
// ★ 空的 default → 非阻塞 → 无限循环!CPU 飙升!
}
}
// ✅ 正确:无 default → 阻塞等待
for {
select {
case v := <-ch:
process(v)
}
}
// ✅ 或添加 sleep
for {
select {
case v := <-ch:
process(v)
default:
time.Sleep(10 * time.Millisecond) // 让出 CPU
}
}
8.2 select 死锁场景
go
// ❌ 死锁1: 空 select
select {} // 永久阻塞,没有任何 goroutine 能唤醒
// ❌ 死锁2: 循环等待
// G1: select { case <-ch2: ... case ch1<-: ... } 等 ch2 才发 ch1
// G2: select { case <-ch1: ... case ch2<-: ... } 等 ch1 才发 ch2
// ★ 虽然 lockorder 防止了 channel 锁死锁,但逻辑层面仍可能死锁
// ✅ 使用 context 打破循环
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-ctx.Done():
return ctx.Err()
case v := <-ch:
process(v)
}
8.3 time.After 内存泄漏
go
// ❌ 泄漏:每次循环创建新 timer
for {
select {
case v := <-ch:
process(v)
case <-time.After(5 * time.Second):
// ★ 每次 select 都创建新 timer
// 如果 ch 频繁有数据,timer 不会被 GC 直到触发
fmt.Println("timeout")
}
}
// ✅ 修复:复用 timer
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
for {
select {
case v := <-ch:
process(v)
if !timer.Stop() {
<-timer.C // 排空 channel
}
timer.Reset(5 * time.Second)
case <-timer.C:
fmt.Println("timeout")
timer.Reset(5 * time.Second)
}
}
8.4 select case 数量限制
go
// ★ 最多 65536 个 case(uint16 索引限制)
// 实际上超过几十个 case 就应该重构代码
// ❌ 过多 case
select {
case <-ch1: ...
case <-ch2: ...
// ... 1000 个 case ...
}
// ✅ 重构:使用 reflect.Select 或 map 分发
9 reflect.Select 动态 select
go
// reflect.Select 允许运行时动态构建 select
cases := []reflect.SelectCase{
{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch1)},
{Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch2), Send: reflect.ValueOf(42)},
{Dir: reflect.SelectDefault},
}
chosen, value, ok := reflect.Select(cases)
// chosen: 选中 case 的索引
// value: 接收到的值
// ok: 接收是否成功
底层调用:
go
// reflect.rselect → runtime.reflect_rselect
func reflect_rselect(cases []runtimeSelect) (int, bool) {
if len(cases) == 0 { block() }
// 构建 scase 数组
// 调用 selectgo
// 返回选中的索引
}
10 总结:Select 核心知识图谱
#mermaid-svg-3Jn2Js5fhppC1UQs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3Jn2Js5fhppC1UQs .error-icon{fill:#552222;}#mermaid-svg-3Jn2Js5fhppC1UQs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3Jn2Js5fhppC1UQs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3Jn2Js5fhppC1UQs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3Jn2Js5fhppC1UQs .marker.cross{stroke:#333333;}#mermaid-svg-3Jn2Js5fhppC1UQs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3Jn2Js5fhppC1UQs p{margin:0;}#mermaid-svg-3Jn2Js5fhppC1UQs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster-label text{fill:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster-label span{color:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster-label span p{background-color:transparent;}#mermaid-svg-3Jn2Js5fhppC1UQs .label text,#mermaid-svg-3Jn2Js5fhppC1UQs span{fill:#333;color:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs .node rect,#mermaid-svg-3Jn2Js5fhppC1UQs .node circle,#mermaid-svg-3Jn2Js5fhppC1UQs .node ellipse,#mermaid-svg-3Jn2Js5fhppC1UQs .node polygon,#mermaid-svg-3Jn2Js5fhppC1UQs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3Jn2Js5fhppC1UQs .rough-node .label text,#mermaid-svg-3Jn2Js5fhppC1UQs .node .label text,#mermaid-svg-3Jn2Js5fhppC1UQs .image-shape .label,#mermaid-svg-3Jn2Js5fhppC1UQs .icon-shape .label{text-anchor:middle;}#mermaid-svg-3Jn2Js5fhppC1UQs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3Jn2Js5fhppC1UQs .rough-node .label,#mermaid-svg-3Jn2Js5fhppC1UQs .node .label,#mermaid-svg-3Jn2Js5fhppC1UQs .image-shape .label,#mermaid-svg-3Jn2Js5fhppC1UQs .icon-shape .label{text-align:center;}#mermaid-svg-3Jn2Js5fhppC1UQs .node.clickable{cursor:pointer;}#mermaid-svg-3Jn2Js5fhppC1UQs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3Jn2Js5fhppC1UQs .arrowheadPath{fill:#333333;}#mermaid-svg-3Jn2Js5fhppC1UQs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3Jn2Js5fhppC1UQs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3Jn2Js5fhppC1UQs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3Jn2Js5fhppC1UQs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3Jn2Js5fhppC1UQs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3Jn2Js5fhppC1UQs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster text{fill:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs .cluster span{color:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3Jn2Js5fhppC1UQs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3Jn2Js5fhppC1UQs rect.text{fill:none;stroke-width:0;}#mermaid-svg-3Jn2Js5fhppC1UQs .icon-shape,#mermaid-svg-3Jn2Js5fhppC1UQs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3Jn2Js5fhppC1UQs .icon-shape p,#mermaid-svg-3Jn2Js5fhppC1UQs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3Jn2Js5fhppC1UQs .icon-shape .label rect,#mermaid-svg-3Jn2Js5fhppC1UQs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3Jn2Js5fhppC1UQs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3Jn2Js5fhppC1UQs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3Jn2Js5fhppC1UQs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Select 核心知识
编译器三种优化
0case/1case/2case
selectgo 三阶段
Pass1/Pass2/Pass3
pollorder 随机化
Fisher-Yates 洗牌
lockorder 排序
按 ch 地址 防死锁
公平性保证
随机轮询顺序
nil channel
永不选中
isSelect + CAS
防重复唤醒
selparkcommit
挂起前解锁
常见陷阱
空default/time.After泄漏
selectgo 三阶段一图总结:
┌────────────────────────────────────────────────────────────┐
│ selectgo 执行流程 │
├──────────┬─────────────────────────────────────────────────┤
│ Phase 0 │ pollorder 随机洗牌 + lockorder 堆排序 + sellock │
├──────────┼─────────────────────────────────────────────────┤
│ Pass 1 │ 按 pollorder 检查就绪 │
│ │ recv: sendq? → bufrecv? → closed? │
│ │ send: closed? → recvq? → bufsend? │
│ │ 有就绪 → 执行 → 返回 │
│ │ 无就绪 + default → 返回 -1 │
├──────────┼─────────────────────────────────────────────────┤
│ Pass 2 │ 无就绪 + 无 default → 注册等待 │
│ │ 每个 case 创建 sudog → 加入 channel 等待队列 │
│ │ gopark 挂起 → selparkcommit 解锁 │
├──────────┼─────────────────────────────────────────────────┤
│ Pass 3 │ 被唤醒 → sellock 重新加锁 │
│ │ 找到唤醒者 sudog → 记录 casi │
│ │ 从其他 channel 移除 sudog → releaseSudog │
│ │ selunlock → 返回 (casi, recvOK) │
└──────────┴─────────────────────────────────────────────────┘
源码索引
文件 关键内容 runtime/select.go:16scase结构体、sellock/selunlock、selparkcommitruntime/select.go:116selectgo------三阶段核心实现runtime/select.go:556reflect_rselect------reflect 动态 selectruntime/select.go:624dequeueSudoG------从等待队列移除指定 sudogruntime/chan.go:159chansend------channel 发送runtime/chan.go:462chanrecv------channel 接收cmd/compile/internal/walk/select.go编译器三种优化重写 + scase 类型定义