type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // 元素类型指针
sendx uint // 发送操作的下标
recvx uint // 接受操作的下标
recvq waitq // 接受等待队列
sendq waitq // 发送等待队列
// 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
}
- chan是golang中协程之间通信的结构,所以必定要有一个锁lock来控制并发
- 有缓冲的chan需要使用buf来指向缓冲区的位置
- qcount记录队列中的总数据量,dataqsiz用来记录队列的容量,elemtype用来记录元素类型,便于内存复制、垃圾回收等等操作
- 因为chan的通信操作注定要有等待的情况,所以需要recvq,sendq用来存储等待发送或接受的协程,recvx,sendx用来记录发接受或发送操作的下标。
recvq和sendq的类型waitq中包含了头尾两个sudog类型的变量,sudog为通用的等待队列结构。
type waitq struct {
first *sudog
last *sudog
}
type sudog struct {
// The following fields are protected by the hchan.lock of the
// channel this sudog is blocking on. shrinkstack depends on
// this for sudogs involved in channel ops.
g *g
next *sudog
prev *sudog
elem unsafe.Pointer // data element (may point to stack)
// The following fields are never accessed concurrently.
// For channels, waitlink is only accessed by g.
// For semaphores, all fields (including the ones above)
// are only accessed when holding a semaRoot lock.
acquiretime int64
releasetime int64
ticket uint32
// isSelect indicates g is participating in a select, so
// g.selectDone must be CAS'd to win the wake-up race.
isSelect bool
// success indicates whether communication over channel c
// succeeded. It is true if the goroutine was awoken because a
// value was delivered over channel c, and false if awoken
// because c was closed.
success bool
parent *sudog // semaRoot binary tree
waitlink *sudog // g.waiting list or semaRoot
waittail *sudog // semaRoot
c *hchan // channel
}
这是一个包含next和prev的双向链表,里面就包含了等待的chan信息
chan的元素缓冲区就是一个数组,通过元素下标的改变实现环形结构
if c.sendx == c.dataqsiz {
c.sendx = 0
}
在执行发送操作的时候,如果接受等待队列中存在等待的协程,则直接将元素发送给等待协程,从而绕过缓冲区,避免了不必要的存取操作。