本文档主要通过实际例子,GO版本v1.16.6,结合Go channel的数据结构进行分析,hchan里面的参数是怎么变化的,同时解析一下hchan里面buf的读与写,queue是怎么运作的。想要了解Go Channel源码的可以看,我之前的博客《GO Channel源码分析》。
本文大纲如下,会先分析流程,数据结构,最后再实例:
- 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,写入