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中。
完整的发送流程以下:ui
一样的,当作从channel中接收数据的动做时,会先判断buf是否为空,为空的话进行现场打包成sudog链在recvq的链表上。
完整的接收流程以下:
与发送流程有所不一样的是,当buf数据满而且sendq中有sudog的时候,咱们还须要判断是否有缓冲区。this
若是有缓冲区的话咱们须要维护buf:spa
咱们能够从接收流程中发现,咱们会在buf为空的时候才会往recvq追加sudog,那么也就是说在接收流程中,recvq只要不为空那就说明buf是空的,那么没有缓冲区和有缓冲区也都是等价于空的buf,因此无需判断。
可是在接收流程中,若是sendq不为空的话。指针
挂起与唤醒:code