channel分为有buffer的和没有buffer的。
没有buffer的可以当成有buffer但是buffersize为0的情况。
buffer数据结构:
type hchan struct {
qcount uint // 当前chan中有多少数据
dataqsiz uint // 环形数组队列的大小,也就是我们定义的缓冲区大小
buf unsafe.Pointer // 指向环形数组队列的指针
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // 发送时插入的位置(环形数组的下标)
recvx uint // 接收时取数据的位置(环形数组的下标)
recvq waitq // 接收链表,当buf为空的时候,打包goroutine现场后放在这里
sendq waitq // 发送链表,当buf满的时候,打包goroutine现场后放在这里
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G‘s status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
像图中发送数据到channel中,每次qcount和sendx会随之变化,sendx会在插入前标志当前的插入位置变到插入后标志下一个数据插入位置(由于是环形数组,所以如果在最后位置插入后索引归0)
当buf里面的数据满的时候,再往里面发送数据,此时qcount==dataqsize表示满,此时我们会将当前G的现场与channel打包成一个sudog的结构,链在sendq上。
我们知道,sendq中存放的是等待发送的sudog,那么同样的recvq存放的就是等待接收的sudog。可以想象到,当recvq中有sudog节点的时候就说明我们的缓冲区已经没有数据可以取了,才会将接收的g放到recvq中。此时,我们需要发送的内容应该立即被拿走,不该再放到buf或sendq中。
完整的发送流程如下:
同样的,当做从channel中接收数据的动作时,会先判断buf是否为空,为空的话进行现场打包成sudog链在recvq的链表上。
完整的接收流程如下:
与发送流程有所不同的是,当buf数据满并且sendq中有sudog的时候,我们还需要判断是否有缓冲区。
我们可以从接收流程中发现,我们会在buf为空的时候才会往recvq追加sudog,那么也就是说在接收流程中,recvq只要不为空那就说明buf是空的,那么没有缓冲区和有缓冲区也都是等价于空的buf,所以无需判断。
但是在接收流程中,如果sendq不为空的话。
原文:https://www.cnblogs.com/codexiaoyi/p/15139123.html