首先说一些结论:只有发送端可以close chan,接收端不要搞
1、重复 close,产生 panic
2、close一个nil,产生 panic
3、写未初始化chan永远阻塞
4、写close会panic
5、make chan返回的是* hchan,所以chan可以随意在函数和go间传递
channel 的主要结构:
一个环形数组实现的队列,用于存储消息元素;
两个链表实现的 goroutine 等待队列,用于存储阻塞在 recv 和 send 操作上的 goroutine;
一个互斥锁,用于各个属性变动的同步
channel make 实现
当元素不含指针的时候,会将整个 hchan 分配成一个连续的空间。
channel send
send 有以下几种情况:
- 写未初始化chan永远阻塞,写close会panic
- 有 goroutine 阻塞在 channel recv 队列上,此时缓存队列为空,直接将消息发送给 reciever goroutine,只产生一次复制
- 当 channel 缓存队列有剩余空间时,将数据放到队列里,等待接收,接收后总共产生两次复制
- 当 channel 缓存队列已满时,将当前 goroutine 加入 send 队列并阻塞。
channel recieve
- 从已经 close 且为空的 channel recv 数据,返回空值及false
- send 队列不为空
- 缓存队列为空(证明是无缓冲chan),直接从 sender recv 元素
- 缓存队列不为空,此时只有可能是缓存队列已满,从队列头取出元素,并唤醒 sender 将元素写入缓存队列尾部
- 缓存队列不为空,直接从队列取元素,移动头索引(cnt- -, recv++)
- 缓存队列为空,将 goroutine 加入 recv 队列,并阻塞
channel close
- 重复 close,产生 panic
- 唤醒所有 reciever, (这里被唤醒的go会去读channel,然后get到空值及false)
- 唤醒所有 sender(理论上不可能有,so),产生 panic