Go Channel 实例剖析
本文档主要通过实际例子,GO 版本 v1.16.6,结合 Go channel 的数据结构进行分析,hchan 里面的参数是怎么变化的,同时解析一下 hchan 里面 buf 的读与写,queue 是怎么运作的。想要了解 Go Channel 源码的可以看,我之前的博客《GO Channel 源码分析》https://xie.infoq.cn/article/f378a432b07f54af086c9e7ae
本文大纲如下,会先分析流程,数据结构,最后再实例:
channel send 流程图
channel read 流程图
hchan.buf 的读与写
hchan.recvq/sendq 的读与写
channel send 实例分析
channel recv 实例分析
channel send
判断 channel recvq 是否有正在等着 read 的 channel
如果有从 recvq 中 dequeue 一个出来 sudog(sg)
如果 sg 指向的 element 不为空,则把当前写入的 channel 的 value 给 sg,send 的数据直接拷贝到目标栈上
如果 sg 指向的 element 已经为空或者执行完第三步,则唤醒之前 park 住的 sg
如果第一步 recvq 为空,表示没有在等待 read 的 channel,则判断是否当前 channel 有缓存空间
有缓存空间则写入 buf 中
没有缓存空间则初始化一个 sudog,写入 sendq,并且 gopark 挂起
channel read
和 channel send 大同小异
判断 channel sendq 是否有正在等着 send 的 channel
如果有从 sendq 中 dequeue 一个出来 sudog(sg)
如果当前 channel 还有缓存空间,从 buf 中 copy
如果当前 channel 没有缓存空间,则直接把 recv 的数据 copy 给 sg,从 sender 的栈上拷贝数据
如果第一步 sendq 为空,表示没有在等待 send 的 channel,判断否当前 channel 有缓存空间
有缓存空间则从 buf 中读取
没有缓存空间则初始化一个 sudog,写入 recvq,并且 gopark 挂起
hchan.buf 读与写
channel 的 buf 其实就是一个环形队列,channel send 和 recv,分别是对 buf 的 write 和 read。write 的时候就是把写入 value 的地址 copy 到 buf 中对应的缓存地址去。read 就是把 buf 在源码中是通过 chanbuf 来计算出 read 和 write 的地址。
在 hcan 中,datasize,qcount,sendx 和 recvx 就是用来操作 buf 的参数。datasize 是 buf 的空间大小,qcount 是存储元素的数量,sendx 是写入时的地址下标,recvx 是读取时的地址下标。
hchan.recvq/sendq 的读与写
hchan 里面的 recvq 和 sendq 就是一个先进先出的 queue,enqueue 和 dequeue 可以通过下面的例子看一下。
channel send 实例分析
以c:=make(chan int,2)
,chan type 为 int,缓存大小为 2
同一个颜色的就是一步
c < 1:有缓存空间,写入缓存
c < 1:缓存空间,写入缓存
c < 1:缓存空间用完,gopark,写入 sendq
channel recv 实例分析
接着上个例子的结果
= < c:sendq 中有值,dequeue 出来,写入 dequeue 的 channel
= < c:缓存中有值,从缓存中读
= < c:缓存中还有值,从缓存中读
= < c:channel 没有缓存了,就写入 recvq,gopark
c < 1:recvq 中有值,从 recvq dequeue,写入
评论