简介
获取 channel 元素有多种,这里说使用遍历获取 channel 元素,当 channel 没有关闭时,遍历 channel 会出现阻塞或者死锁,下面分别看这两种情况
阻塞
运行一下程序,会发现 consume 函数里的 fmt.Println(“end”) 不会执行,因为channel 未关闭,一直在获取 channel 元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func main() {
c := make(chan int, 3)
product(c)
go consume(c)
time.Sleep(6 * time.Second)
}
func product(c chan int) {
c <- 0
c <- 1
c <- 2
}
func consume(c chan int) {
for mag := range c {
fmt.Println(mag)
}
fmt.Println("end")
}
|
运行结果:
但是当把生产者生产完后,关闭 channel,发现不会阻塞,会执行 fmt.Println(“end”)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func main() {
c := make(chan int, 3)
product(c)
go consume(c)
time.Sleep(6 * time.Second)
}
func product(c chan int) {
c <- 0
c <- 1
c <- 2
close(c)
}
func consume(c chan int) {
for mag := range c {
fmt.Println(mag)
}
fmt.Println("end")
}
|
运行结果:
死锁
当 channel 未关闭遍历 channel 且所有协程都阻塞时,会发生死锁,上面没发生死锁,是因为在必须所有协程都阻塞才会死锁,上面程序只是 consume() 阻塞了,主协程并没有阻塞,所以没有死锁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func main() {
c := make(chan int, 3)
product(c)
go consume(c)
// 使得主协程阻塞
select {}
}
func product(c chan int) {
c <- 0
c <- 1
c <- 2
//close(c)
}
func consume(c chan int) {
for mag := range c {
fmt.Println(mag)
}
fmt.Println("end")
}
|
运行结果:
1
2
3
4
|
0
1
2
fatal error: all goroutines are asleep - deadlock!
|
生产者关闭 channel,则正常运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func main() {
c := make(chan int, 3)
product(c)
go consume(c)
// 这里不能使用 select{},因为主协程阻塞,其他协程都执行完了,最终死锁
time.Sleep(3 * time.Second)
}
func product(c chan int) {
c <- 0
c <- 1
c <- 2
close(c)
}
func consume(c chan int) {
for mag := range c {
fmt.Println(mag)
}
fmt.Println("end")
}
|
正常
还有一种情况,当生产者与消费者都开启协程,即使 channel 没有关闭,也不会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func main() {
c := make(chan int, 3)
go product(c)
go consume(c)
time.Sleep(3 * time.Second)
}
func product(c chan int) {
c <- 0
c <- 1
c <- 2
//close(c)
}
func consume(c chan int) {
for mag := range c {
fmt.Println(mag)
}
fmt.Println("end")
}
|
运行结果:
select
这里简单说下 select 用法,select 用于获取 channel 元素,也可以阻塞协程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func main() {
bufChan := make(chan int)
go func() {
for{
bufChan <-1
time.Sleep(time.Second)
}
}()
go func() {
for{
fmt.Println(<-bufChan)
}
}()
select{}
}
|
这样主函数就永远阻塞住了,这里要注意上面一定要有一直活动的goroutine
,否则会报deadlock
关注微信公众号,可了解更多云原生详情~