GO并发 (Goroutine)

GO并发(Goroutine),带缓冲信道和无缓冲信道(Channel), 多路复用(select),关闭信道(close)

信道

带缓冲信道代码

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
package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan int, 2)
// ch := make(chan int) // 无缓冲信道
over := make(chan bool)
go func() {
for i := 1; ; i++ {
fmt.Println("开始", i)
time.Sleep(time.Second * 2)
fmt.Println("-------等待2秒---------")
j, ok := <-ch
if ok {
fmt.Println("已接收", j)
} else {
fmt.Println("已全部接收")
over <- true
return
}
}
}()
// time.Sleep(time.Second * 5)
for j := 1; j <= 3; j++ {
ch <- j
fmt.Println("发送", j)
}
close(ch)
fmt.Println("已全部发送, 信道关闭")
<-over
}

带缓冲信道执行结果(发送等待5秒)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
开始 1
-------等待2秒---------
发送 1
发送 2
发送 3
已全部发送, 信道关闭
已接收 1
开始 2
-------等待2秒---------
已接收 2
开始 3
-------等待2秒---------
已接收 3
开始 4
-------等待2秒---------
已全部接收

带缓冲信道执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
发送 1
发送 2
开始 1
-------等待2秒---------
已接收 1
开始 2
发送 3
已全部发送, 信道关闭
-------等待2秒---------
已接收 2
开始 3
-------等待2秒---------
已接收 3
开始 4
-------等待2秒---------
已全部接收

无缓冲信道执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
开始 1
-------等待2秒---------
已接收 1
开始 2
发送 1
-------等待2秒---------
已接收 2
开始 3
发送 2
-------等待2秒---------
已接收 3
开始 4
发送 3
已全部发送, 信道关闭
-------等待2秒---------
已全部接收

多路复用

多路复用(select)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"time"
)

func main() {
timeout := make (chan bool)
go func() {
time.Sleep(1e9) // sleep one second
timeout <- true
}()
ch := make (chan int)
select {
case <- ch:
case <- timeout:
fmt.Println("timeout!")
return
}
}

执行结果

1
timeout!

select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行

default

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
package main

import (
"fmt"
"time"
)

func main() {
timeout := make (chan bool)
go func() {
time.Sleep(1e9) // sleep one second
timeout <- true
}()
ch := make (chan int)
for {
select {
case <- ch:
case <- timeout:
fmt.Println("timeout!")
return
default:
fmt.Println("def")
}
}
}

执行结果

1
2
3
def
// ...
timeout!

当 select 中的其它分支都没有准备好时,default 分支就会执行。

close

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
)

func fibonacci(n int, c chan int) {
for i := 0; i < n; i++ {
c <- i
}
close(c)
}

func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}

循环 for i := range c 会不断从信道接收值,直到它被关闭。

执行结果

1
2
3
0
// ...
9