[toc]
Channel
编译器翻译
关键数据结构
- hchan
- sudog
hchan
<img src="http://picgo.vipkk.work/20200504175832.png" alt="image-20200504175832482" style="zoom:50%;" />
sudog
<img src="http://picgo.vipkk.work/20200505204907.png" alt="image-20200505204907061" style="zoom:50%;" />
其中buf + sendx + recvx组成ring buffer.
sendq 和 recvq是双向链表,用于储存等待的g。
sudog中的elem是chan用来传递的元素
源码
init
- 判读是否溢出,溢出则直接panic
- 如果要申请的内存为0,则把buf 置为racebuf
- 如果内存不为0,但buf中元素不是指针,则内存分配在stack上与chan连续
- 否则分配到堆上
- 实例化 数据类型和缓存大小
send
所有的返回都会unlock
- 如果chan为nil,则返回
- fasepath,快速失败(TODO)
- 获取锁
- 如果此时有receiver在等待,则直接发给他over,返回发送成功
- 需要将对应的receiver 执行goready
- 否则就判断此时buf是否已满,不满的话放入,更新ringbuffer指针
- 需要将对应的receiver 执行goready
- 如果buf已满,将g包装成sudog,然后park等待
- park恢复,释放内存,释放sudog
recv
- 如果chan为nil,则返回
- fasepath,快速失败(TODO)
- 获取锁
- 如果此时有sender在等待,则直接发给他over,返回发送成功
- 需要将对应的sender 执行goready
- 否则就判断此时缓存中是否有数据,有的话就执行内存copy,更新ringbuffer指针
- 需要将对应的sender 执行goready
- 如果缓存为空,则把g包装为sudog,然后park等待。
- park恢复,释放内存,释放sudog
close
- 如果chan为nil,则返回
- 加锁
- 判断是否已经关闭
- 把 recvq 和 sendq中的sudog取出到glist
- 解锁
- 依次ready glist
挂起和唤醒
当发送行为或者接收行为不能被满足的时候,会
挂起
。(并非全部,也有非阻塞实现,先跳过)当以上行为得到满足的时候,会被
唤醒
。
发送的时候如果已满或者没有缓存的时候,会把g包装成sudog,然后放到chan中,park,等有recv来之后,会把发送的sudog取出,执行goready。(接受同理)
几种常见行为
行为 | 表现 |
---|---|
发送时buf满了 | 阻塞 |
接受时buf没数据 | 阻塞 |
发送是chan close了 | panic |
接收时chan close了 | 返回默认值 |
Select
编译器翻译
<img src="http://picgo.vipkk.work/20200506002833.png" alt="image-20200506002833134" style="zoom:50%;" />
<img src="http://picgo.vipkk.work/20200506002932.png" alt="image-20200506002932143" style="zoom:50%;" />
Code
selectgo是 select中多个case的实现,具体分析如下
// selectgo implements the select statement.
//
// cas0 points to an array of type [ncases]scase, and order0 points to
// an array of type [2*ncases]uint16. Both reside on the goroutine's
// stack (regardless of any escaping in selectgo).
//
// selectgo returns the index of the chosen scase, which matches the
// ordinal position of its respective select{recv,send,default} call.
// Also, if the chosen scase was a receive operation, it reports whether
// a value was received.
func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
return 0,false
}
流程
nil处理
-
shuffle打乱(保证case的随机性)
// generate permuted order for i := 1; i < ncases; i++ { j := fastrandn(uint32(i + 1)) pollorder[i] = pollorder[j] pollorder[j] = uint16(i) }
按照hchan 的address进行heap sort 得到lockorder, 这个顺序是对channel加锁的顺序 防止死锁
按照lockorder的顺序依次对 channel加锁
-
按照pollorder顺序依次遍历ncases
- 如果为nil则继续
- 接收 - > recv
- 发送 -> send
- default
如果都没有的话则继续
按照lock顺序把所有的chan放到对应的 等待队列
park等待唤醒
继续加锁
把未成功执行的chan 出队列
解锁
参考资料
问题
- releasetime
有疑问加站长微信联系(非本文作者)