0x01 问题
阻塞类型chan就是使用make
时,只指定一个类型,不指定长度,或者长度指定为0。
它有如下要求:
- 发送方和接受方都准备好时,才能工作。
所以想到了如下场景:
发送方和使用方都使用select
非阻塞方式,会有什么现象?
即如下代码:是否会有输出?
package main
import "fmt"
import "time"
func main() {
c := make(chan int)
go func() {
for {
select {
case a := <-c:
fmt.Println(a)
default:
}
}
}()
go func() {
for {
select {
case c <- 1:
fmt.Println("sended")
default:
}
}
}()
for {
time.Sleep(100)
}
}
实验结果大家可能吃了一惊,居然没有任何输出。
0x02 解释
这个问题也问题也容易解析。非阻塞通道要求,双方都准备好了,才能工作。示例代码中,两个协程 都是在尝试下,发现不行,就走了。所以,刚好某个时刻两者同时尝试了,一方才能发送成功,一方才能收到。
想象AB两个人在打电话。拨一下就挂,别外一方要刚好在点接听键,才能打过去。
- A拨一下,没接哦,那我挂了,再拨一下。。。
- B点一下接听,没打,哦那算了,我再点一下接听。。。
两个人就这样一直在折腾,电话却一直没打通。。。
整个实现虽然是在研究(play with)通道,但因为Go语言也没有限制这样用法,Go运行时有检查死锁,这种场景也不属于死锁,所以真实场景可能有人会写出这样的代码。那就会有大问题了。
0x03 小结
这个起源也是出于对Go原理的好奇,经过实验发现这里确实有坑。希望小伙伴们不要踩到。