golang channel有没buffer的区别
初学golang的很多人对buffer大小为0和1的channel都不了解。
下面通过make(chan bool)和make(chan bool, 1)的例子说明它们的区别。
说明
引用官方channel的介绍
If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives).
其实官网说得很明白。如果make时,容量传0或者不传的话,即创建一个没有buffer的
channel,它们只有发送方和接收方都准备好了,才能通信成功。不然,只要buffer不满就
可以立即发送,或者不空就可以立即接收。
例子
1 没有buffer,只有发送端
我们通过例子来看一下
func chan0SendOnly() {
c0 := make(chan bool)
log.Println("c0, sending")
c0 <- true
log.Println("c0, send success")
}
func main() {
go chan0SendOnly()
time.Sleep(1 * time.Second)
}
创建一个没有buffer的chan,然后在发送一个true到chan里,发送前后打一个log。main函 数调用,然后sleep 1秒,让子goroutine有运行的机会。
运行结果如下:
可以看到只有sending的日志,success的没有输出。因为没有准备好的channel接收端,所
所就一直阻塞在c0<-true上了。一直到main退出,它就退出了。
2 没有buffer,同时有发送和接收端
再来看一下同时开启send和receive的情况
func chan1SendRecv() {
c1 := make(chan bool)
go func() {
log.Println("c1, sending")
c1 <- true
log.Println("c1, send success")
}()
go func() {
log.Println("c1, receiving")
time.Sleep(2 * time.Second) // sleep 2 second
<-c1
log.Println("c1, recv success")
}()
}
func main() {
go chan1SendRecv()
time.Sleep(3 * time.Second)
}
这个例子我们同时启动了两个goroutine,一个用于发送,一个用于接收,但是我们在接收 之前先sleep了2秒。
运行结果:
可以看到,sending和receiving都同时打出来了,因为两个goroutine的并行执行,所以两
个打出来的顺序不确定,所以可能你的运行结果是先打了sending。过了两秒后,两个success
才同时打出来。看回我们的代码,在接收的goroutine中,我们并没有sleep,而是直接就发
送了,只是在接收的goroutine中sleep了2秒。
所以可以很明显的感觉到,只有receiver也准备好了,sender才会执行,不然就阻塞在那里 了。
3 有buffer,只有发送端
我们来看一下buffer大小为1的channer的情况。 首先看一下只有发送的例子
func chan2SendOnly() {
c2 := make(chan bool, 1)
log.Println("c2, sending")
c2 <- true
log.Println("c2, send success")
}
func main() {
go chan2SendOnly()
time.Sleep(1 * time.Second)
}
这个例子跟第1个差不多,只是我们传大小为1。
运行结果:

可以看到这个跟第一个的比对,这个例子success的log也打出来了。说明了这个channel并 不会阻塞,只在有容量,它立马就发送成功了。
4 有buffer,同时有发送和接收端
再来看一下buffer容量为1,同时发送和接收的例子:
func chan3SendRecv() {
c3 := make(chan bool, 1)
go func() {
log.Println("c3, sending")
c3 <- true
log.Println("c3, send success")
}()
go func() {
log.Println("c3, receiving")
time.Sleep(2 * time.Second) // sleep 2 second
<-c3
log.Println("c3, recv success")
}()
}
func main() {
go chan3SendRecv()
time.Sleep(3 * time.Second)
}
这个例子跟第2个例子也很像,只是传了buffer大小为1。
运行结果:
再看一下这个的输出,sending和send success同时输出了,receiving也是同时输出了。
我们在receive里sleep了2秒,所以2秒后才recv success。
再次证明了,只要buffer不满,sending就立马成功了。
对比
下面进行对比一下:
| chan0SendOnly | chan1SendRecv | chan2SendOnly | chan3SendRecv |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| 没buffer的发送,失败 | 没buffer的发送接收,成功,但是发送要等待接收端准备好 | 有buffer的发送,立即成功 | 有buffer的发送接收成功,发送不用等待接收端准备好 |
以上图片点击即可放大
例子代码传送门gist