go select与channel操作

select和switch类似,不同的是case内容必须是接收io执行结果(例如<-channel),且不需要break。
具体执行逻辑如下:

  1. 如果除default之外,只有一个case满足,则执行case内容
  2. 如果除default之外有多个case满足,则伪随机执行一个case内容
  3. 如果default之外的case都不满足,则执行default内容
  4. 如果没有default,所有case都不满足,则阻塞直到有一个case条件满足

select与channel使用demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func main() {
c1 := make(chan string)
c2 := make(chan string)
c3 := make(chan string)
var i1 string
s2 := "c2"
go func() {
sec := 0
for {
<-time.After(1 * time.Second)
sec++
fmt.Printf("========第%d秒========\n", sec)
}
}()
go func() {
<-time.After(1050 * time.Millisecond)
c1 <- "c1"
<-time.After(1 * time.Second)
c3 <- "c3"
<-time.After(1 * time.Second)
c1 <- "c1'"

<-time.After(3 * time.Second)
fmt.Println(<-c2)
}()

go func() {
i := 5
for i > 0 {
i--
timeout := time.After(2 * time.Second)
select {
case i1 = <-c1:
fmt.Printf("从c1收到%s\n", i1)
case c2 <- s2:
fmt.Printf("发送%s到 c2\n", s2)
case i3, ok := <-c3:
if ok {
fmt.Printf("从c3收到%s\n", i3)
} else {
fmt.Printf("c3 已关闭")
}
case <-timeout:
fmt.Println("等待超时")
}
}
}()

time.Sleep(8 * time.Second)
fmt.Printf("结束测试\n")
}

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
========第1秒=========
从c1收到c1
========第2秒=========
从c3收到c3
========第3秒=========
从c1收到c1'
========第4秒=========
========第5秒=========
等待超时
========第6秒=========
c2
发送c2到 c2
========第7秒=========
结束测试

通过done channel退出goroutine

1
2
3
4
5
6
7
8
9
10
11
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任务已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任务完成
<-done
}
参考资料

Go语言并发模型:使用 select
https://segmentfault.com/a/1190000006815341

Go Channel 详解
https://colobu.com/2016/04/14/Golang-Channels/