Skip to content

Commit be3654b

Browse files
committed
update select
1 parent 7bc6bb8 commit be3654b

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

select.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,82 @@ select {
2424
println("can not be printed")
2525
```
2626

27+
for select 组合起来用很常见,不过需要注意,下面的两种场景可能会造成问题:
28+
29+
```go
30+
for {
31+
select {
32+
case d := <-ch:
33+
default:
34+
}
35+
}
36+
```
37+
38+
这种写法比较危险,如果 ch 中没有数据就会一直死循环 cpu 爆炸。
39+
40+
```go
41+
for {
42+
select {
43+
case d := <-ch:
44+
}
45+
}
46+
```
47+
48+
你以为这么写就没事了?啊当然,一般情况下还好。但如果 ch 被其它 goroutine close 掉了,那么 d:= <-ch 这种形式就是永远不阻塞,并且会一直返回零值了。如果不想关注这种情况,并且 select 中实际只操作一个 channel,建议写成 for range 形式:
49+
50+
```go
51+
for d := range ch {
52+
// do some happy things with d
53+
}
54+
```
55+
56+
这样 ch 关闭的时候 for 循环会自动退出。
57+
58+
如果 select 中需要监听多个 channel,并且这些 channel 可能被关闭,那需要老老实实地写双返回值的 channel 取值表达式:
59+
60+
```go
61+
outer:
62+
for {
63+
select {
64+
case d, ok := <-ch1:
65+
if !ok {
66+
break outer
67+
}
68+
case d, ok := <-ch2:
69+
if !ok {
70+
break outer
71+
}
72+
}
73+
}
74+
```
75+
76+
当然,如果你不确定,可以用下面的 demo 进行验证:
77+
78+
```go
79+
package main
80+
81+
import "time"
82+
83+
func main() {
84+
var ch1 chan int
85+
var ch2 = make(chan int)
86+
close(ch2)
87+
go func() {
88+
for {
89+
select {
90+
case d := <-ch1:
91+
println("ch1", d)
92+
case d := <-ch2:
93+
println("ch2", d)
94+
}
95+
}
96+
}()
97+
time.Sleep(time.Hour)
98+
}
99+
```
100+
101+
尽管 ch2 已经被关闭,依然会不断地进入 case d:= <-ch2 中。因此在使用 for select 做设计时,请务必考虑当监听的 channel 在外部被正常或意外关闭后会有什么样的后果。
102+
27103
## 源码分析
28104

29105
### 数据结构

0 commit comments

Comments
 (0)