channel底层数据结构
| 1 | type hchan struct { | 
其中需要注意的是:
- 
buf指向底层循环数组,如果是非缓冲则指向hchan地址
- 
sendq,recvq分别表示被阻塞的 goroutine,这些 goroutine 由于尝试读取 channel 或向 channel 发送数据而被阻塞(部分通过直接复制memmove的方式传输未被阻塞,也就不在链表中)
- 
waitq是sudog的一个双向链表,而sudog实际上是对 goroutine 的一个封装1 
 2
 3
 4type waitq struct { 
 first *sudog
 last *sudog
 }
- 
lock用来保证每个读 channel 或写 channel 的操作都是原子的
 
chan初创建
chan的创建分为含有缓冲区和非缓冲区,都通过 func makechan(t *chantype, size int64) *hchan实现
| 1 | func makechan64(t *chantype, size int64) *hchan { | 
以上有几点注意:
- 针对创建不同的chan初始化过程
- 非缓冲chan 只需要分配一个hchanSize
- 缓冲区chan 不含有指针,则分配一个连续的内存
- 缓冲区chan 含有指针,分别分配hchan和 指针需要的内存大小
- chan 分配在堆中,返回一个指针*hchan
- sendq 和 recvq 链表由goroutine初始化时候创建,不在这里创建

chan接收
接收的操作有两种写法
| 1 | // entry points for <- c from compiled code | 
- received反应channel是否被关闭
- 接收值会放到elem所指向的指针,如果忽略接收值,则elem为nil
- 第三个参数 都使用 true 表示阻塞模式,
| 1 | /* | 
需要注意的是
- 同一个 chan 不能被重新开启
- 在缓冲区chan buf满了的情况下,发送协程阻塞,则接收者还是会优先处理缓冲区数据
| 1 | //如果是非缓冲型的,就直接从发送者的栈拷贝到接收者的栈 | 
 
chan发送
ch <- 3 最终会转换成 chansend 函数
| 1 | func full(c *hchan) bool { | 
需要注意的是:
- KeepAlive 的作用和原理是?
| 1 | func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) { | 
关闭chan
| 1 | func closechan(c *hchan) { | 
chan应用
- 针对不同的chan会有不同的效果:

- 
控制并发数 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13var limit = make(chan int, 3) 
 func main() {
 // …………
 for _, w := range work {
 go func() {
 limit <- 1
 w()
 <-limit
 }()
 }
 // …………
 }
