chan是一个多生产者和多消费者的模式。
chan数据结构:
- 只有buf存放元素的循环队列的buffer。只有缓冲的chan才有buf,没有缓冲的chan不会创建buf.
- recvx 接受数据的指针在buf中的位置,一旦取出数据,指针就会指向下一个位置。
- sendx 发送数据的指针在buf中的位置,一旦接受新的数据,指针就会加上elesize,移向下一个位置
- sendq 生产者的goroutine 因为buf 满了,阻塞等待的goroutine 就会加入到sendq中。
- recvq 消费者的goroutine因为没有数据可以读取,就会阻塞等待,然后加入到recvq中。
- lock 排它锁
关闭chan
其实chan没有必要去手动close,当没有goroutine引用,最会就会被gc自动回收。关闭chan一般用来通知其他goroutine某个任务已经完成。 关闭过程会发生panic: 1、向一个关闭的chan写数据2、关闭已经关闭的chan3、关闭一个nil的chan
从关闭的chan仍然能读取数据,当读取超过写入数据的数量,就会输出为0。
**记住不要从receiver接收端关闭数据,也不要在有多个sender时,关闭channel。**因为不确定是否有其他sender仍然在写入channel。贸然关闭会导致panic。
优雅关闭 chan解决方案 是增加一个传递关闭信号的channel,reciver通过信号channel下达关闭channel指令。sender使用select监听到信号channel关闭,停止发送数据,直接return。
channel happens before
启动goroutine 的go 语句的执行,一定happen before此goroutine内的代码执行。
四大原则: 第一个:无论是缓冲型channel还是非缓冲型channel,第n个send一定happen before第n个receive的完成。 第二个:对于容量为n的缓冲型channel,第n个receive一定happen before第n+m个send finished。 第三个:对于非缓冲型channel,channel读取数据的调用一定happen before往此channel发送数据的调用完成。 第四个:close channel的调用,肯定happen before 从关闭channel读取零值