这是我参与「第五届青训营 」笔记创作活动的第13天。
最近各大厂陆续开始收暑期实习的简历了,作为Go语言选手自然要好好学习一下Go语言相关知识。
channel相关的源代码在runtime/chan.go中。
type hchan struct {
qcount uint
dataqsiz uint
buf unsafe.Pointer
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
}
channel的底层实现是一个队列,有一段连续的空间,在创建时根据指定的缓冲区大小分配。
- qcount是队列中元素个数
- dataqsiz是队列大小
- buf指向该队列
- elemsize为每个元素大小
- closed表示channel是否被关闭
- elemtype为元素类型
- lock为控制互斥访问的锁
- recvx为队列中待接收的元素位置(类似于队首)
- sendx为往队列中放入元素的为(类似于队尾)
- recvq为等待接收元素的goroutine
- sendq为等待放入元素的goroutine
对于发送数据的过程,在不考虑通道关闭等异常情况下,如果通道有缓存且未满,则待发送的数据被存入缓存中(即底层队列的队尾)。
倘若没有缓存且recvq上没有接收者,或者缓存已满,那么发送的goroutine需要阻塞,挂在sendq上,等待有接收的goroutine取出数据腾出位置。
如果没有缓存,或者缓存为空的情况下recvq上有接收者,那么数据就直接从发送goroutine传给接收goroutine,不需要经过通道缓存,提高效率。
对于接收数据的情况也类似。如果通道有缓存且里面有数据,接收goroutine从中取出数据。
如果没有缓存或者缓存为空,且没有发送者在等待,那么就挂在recvq上等待。
如果缓存为空且有发送者在等待,那么就直接从发送者手上取数据。