Go channel底层机制

此开源图书由ithaiq原创,创作不易转载请注明出处

channe通道源码如下

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 // element type
	sendx    uint   // send index
	recvx    uint   // receive index
	recvq    waitq  // list of recv waiters
	sendq    waitq  // list of send waiters

	// 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
}
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

	// isSelect indicates g is participating in a select, so
	// g.selectDone must be CAS'd to win the wake-up race.
	isSelect bool
	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
	parent      *sudog // semaRoot binary tree
	waitlink    *sudog // g.waiting list or semaRoot
	waittail    *sudog // semaRoot
	c           *hchan // channel
}

hchan结构体解读:

hchan结构体是channel具体实现,其中主要字段buffer是一个循环队列,sendx和recvx用于接收buffer发送和接收的index,lock是互斥锁,sendq和recvq是表示等待阻塞列表。其他字段如qcount是队列剩余数量,dataqsize是缓冲区大小

此开源图书由github(ithaiq)原创,创作不易转载请注明出处

sudog结构解读:

当channel满了之后go协程阻塞后会生成一个sudog结构的数据,其中g是具体goroutine,elem是具体的数据,c是具体的channel通道

  1. 创建channel会在堆中分配内存,并返回指针。

  2. 使用环形循环队列作为缓冲区。

  3. 每次操作channel都会加锁,并更新sendx和recvx的index。

  4. channel缓冲区满进入等待队列,让出M等待被唤醒

  5. 唤醒后重新加入G队列取出数据

最后更新于

这有帮助吗?