File tree Expand file tree Collapse file tree 1 file changed +76
-0
lines changed Expand file tree Collapse file tree 1 file changed +76
-0
lines changed Original file line number Diff line number Diff line change @@ -24,6 +24,82 @@ select {
24
24
println (" can not be printed" )
25
25
```
26
26
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
+
27
103
## 源码分析
28
104
29
105
### 数据结构
You can’t perform that action at this time.
0 commit comments