至于为什么是堆上的一个结构体?自然是因为这种被设计用来实现协程间通信的组件,其作用域和生命周期不可能仅限于某个函数内部,所以golang直接将其分配在堆上。


channel分为“无缓冲”和“有缓冲”两种,对于有缓冲channel来讲,需要有相应的内存来存储数据,实际上就是一个数组。

hchan需要记录数组的地址、容量、元素的大小,以及已有元素个数。

又因为golang运行时中,内存复制、垃圾回收等机制依赖数据的类型信息,所以hchan中还要有一个指针,指向元素类型的类型元数据。



channel支持交替的读写(称send为写,recv为读,更简洁),有缓冲channel内的缓冲数组会被作为一个“环型”来使用,当下标超过数组容量后会

回到第一个位置,所以要分别记录读写下标的位置。



当读和写操作不能立即完成时,需要能够让当前协程在channel上等待,当条件满足时,要能够立即唤醒等待的协程,所以要有两个等待队列,分别针对读和写。


等待队列是runtime.sudog类型的双向链表,sudog中会记录是哪个协程在等待,等待哪一个channel等等。
特别是sudog.elem这个成员,对于recvq中的sudog而言,它代表recv到数据以后要存储到哪里;对于sendq中的sudog而言,它代表要send的数据在哪里。

对ch1而言,目前它的缓冲区已满,接下来读出一个元素,注意观察读写下标的变化。
v <- ch1