go channel

go 社区有一句口号:”不要通过共享内存来通信,而应该通过通信来共享内存”, channel 用于 goroutine 之间传递消息。如果是多进程之间的通信,还是推荐使用 socket 或者 http.

channel 操作

声明和定义

channel 只能发送指定类型的数据,因此channel的声明和定义必须指定类型;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明一个 int类型的 channel
var ch1 chan int

// 定义一个不带缓存的 channel
ch := make(chan int)

// 定义一个带缓存的 channel
ch := make(chan int, 10)

// 获取 channel 容量
cap(ch)

// 获取 channel 现有长度、
len(ch)

channel 的缓存

从无缓存的 channel 中读取消息,会发生阻塞,直到有 goroutine 向该channel 发送消息;同理向无缓存的channle发送消息也会阻塞,直到有 goroutine 读取该channel中的消息。

通过无缓存的channel进行通信时,接受者收到数据 happens before 发送者 goroutine 唤醒

有缓存的 channel 当缓存未满时,发送消息不会阻塞,当缓存慢时,发送消息会阻塞。当缓存不为空时,读取消息不会阻塞,缓存为空时,读取消息会阻塞。

许式伟《go语言编程》中关于 channel 的例子有点小问题,如果是等待 gorotine 完成,建议把写 channel 的操作放在函数的最后,避免 channel 刚写完,就被读取,而其它部分代码还没执行就退出了。

channel 数据发送和读取

1
2
3
4
5
// 写入数据
ch <- 1

// 读取数据
value := <- ch

还可以使用 range 来读取 channel

1
2
3
4
5
ch := make(chan int, 10)

for x := range ch {
fmt.Println(x)
}

range 会一直从 channel 中取值,直到有 goroutine 对该 channel 调用 close 操作。上面的代码等价于:

1
2
3
4
5
6
7
for {
x, ok := <- ch
if !ok {
break
}
fmt.Println(x)
}

close