channel 是 golang 并发编程的极其重要的组成部分之一。但是关于 channel 的一些特性,如果不熟记于心,那么,可是会陷入莫名其妙的问题中无法自拔。下面,是关于 channel 你可能会踩到的雷……
当 channel 为 nil 时...
当你声明一个 channel 并且不进行任何初始化动作的时候,恭喜你,你就获得了一个 nil channel:
1 | var c chan struct{} |
在 golang 中,从一个 nil channel 接收数据,以及往一个 nil channel 发送数据都会天荒地老地阻塞。
这看起来并没有什么。但是,地雷暗藏其中。
举个栗子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import "fmt"
import "time"
func main() {
var c chan bool
go func(){
for d := range c {
fmt.Println("receive a value: ", d)
}
}()
time.Sleep(3*time.Second)
fmt.Println("sleep for a moment and initialize channel c")
c = make(chan bool, 1)
c <- true
fmt.Println("send true to channel")
time.Sleep(3*time.Second)
fmt.Println("Done")
}c
。然后,创建一个 goroutine 来消费 channel c
,并将从中接收到的数据打印出来。等待 3 秒后再对 channel c
进行初始化。接着,往该 channel 发送数据并再度等待 3 秒。
对于上面的代码,我们期望的输出是: 1
2
3
4sleep for a moment and initialize channel c
send true to channel
receive a value: true
Done1
2
3sleep for a moment and initialize channel c
send true to channel
Donec
尚未初始化就在创建的 goroutine 中使用了,所以实际上使用 for...range 的对象是个 nil channel,因此对其的读取操作会永远阻塞。
将初始化放在读取之前,就能获得我们期望的结果。例如:
1 | import "fmt" |