hchan的数据结构
type hchan struct{
qcount int //当前队列中的元素
dataqsize int //环形队列长度
buf unsafe.Pointer //环形队列指针
elemsize uint16 //每个元素的大小
closed uint32 //标识关闭状态
elemtype *_type //元素类型
sendx uint //队列下标,指示元素写入时存放到队列中的位置
recvx uint //队列下标,指示元素从队列的该位置读出
recvq waitq //等待读消息的goroutine队列
sendq waitq //等待写消息的goroutine队列
lock mutex //互斥锁,chan不允许并发读写
}
G的阻塞与唤醒:
chan写入数据的goroutine会唤醒读阻塞的goroutine
chan读取数据的goroutine会唤醒写阻塞的goroutine
向chan中写入数据的过程:
- 如果等待
recvq
接收队列不为空,且缓冲区为空或者没有,则取出recvq
中的G,写入数据,唤醒G,结束发送。 - 如果缓冲区有空余位置,则将数据写入缓冲区,结束发送过程。
- 如果缓冲区没有空余位置,则将数据写入G中,将当前G放入
sendq
中,进入睡眠,等待被读G唤醒。
向chan中读取数据的过程:
- 如果等待
sendq
发送队列不为空,且没有缓冲区,则取出sendq
中的G,读取数据,唤醒G,结束接收过程。 - 如果等待
sendq
发送队列不为空,且缓冲区已满,从缓冲区首部读取数据,从sendq
中取出G,把G的数据写入缓冲区尾部,把G唤醒,结束读取过程。 - 如果缓冲区有数据,则从缓冲区中读出数据,结束读取过程。
- 将当前的G加入到
recvq
中,进入睡眠,等待被写G唤醒。
关闭channel
- 把
recq
中的G全部唤醒,本该写入G的数据位置为nil,把sendq
中的G全部唤醒,但这些G会panic掉。
总结
其实把channel
理解成一条管道,左进右出,当channel
关闭时,左边进入的G全部panic,右边出去的G全部nil。往channel里读写数据时,先看缓存区,再看等待区,缓存有东西则读写缓存中的,没有则读写等待区中的G。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。