1. 解析
1.1 源码
源码路径: https://github.com/golang/go/blob/master/src/runtime/chan.go
1 | type hchan struct { |
- 有一个循环数组,存放有缓冲的channel 数据,size是有缓冲的大小,并且有2个index表示索引值。
- 有两个goroutine 等待队列,一个是接受者等待队列(recvq),和发送者等待队列(sendq),存放sudog。sudog 代表了一个在等待队列中的 goroutine
- 有个锁保证读写 channel 的操作都是原子的,发送和接受数据都会加锁。
1.2 流程
1 | ch := make(chan Task, 3) |
创建channel实际上就是在内存中实例化了一个hchan
的结构体,并返回一个ch指针。
1. 发送数据流程
ch <-task0
ch <-task0
ch <-task0
可以看到sendx 从 0 变成 2,因为是循环数组,满了又变成了0
2. 满了再发送阻塞
当G1 向buf已经满了的 channel 发送数据的时候,调度器会将G1的状态设置为waiting,当G1变为waiting状态后,会创建一个代表自己的sudog的结构,然后放到sendq这个list中。

3. 其他协程取出数据
首先 G2 读取元素之后,将 G1 的状态变为 goready。

然后 G1 从原先的 waiting 状态变为 runnable 状态,然后重新放入 P 的本地队列中,等待调度。

2. 头脑风暴
- 底层只有一个环形数组,有缓冲的channel才有,存放数据。两个index,一个sendx,一个recvx。
- 有一把锁,保护读 channel 或写 channel 的操作都是原子的。
- 两个双向链表队列,一个sendq,一个recvq,上面存着阻塞的G化身的sudog。
- 如果G阻塞,主动调用Go的调度器,G会抽象成一个代表自己的 sudog,挂在上面的队列上。