Channel
|
|
- Channel 本质上是由三个 FIFO(First In FirstOut,先进先出)队列组成的用于协程之间传输数据的协程安全的通道;FIFO 的设计是为了保障公平,让事情变得简单,原则是让等待时间最长的协程最有资格先从 channel 发送或接收数据;
- 三个 FIFO 队列依次是 buf 循环队列,sendq 待发送者队列,recvq 待接收者队列。buf 循环队列是大小固定的用来存放 channel 接收的数据的队列;sendq 待发送者队列,用来存放等待发送数据到 channel 的 goroutine 的双向链表,recvq 待接收者队列,用来存放等待从 channel 读取数据的 goroutine 的双向链表;sendq 和 recvq 可以认为不限大小;
- 跟函数调用传参本质都是传值一样,channel 传递数据的本质就是值拷贝,引用类型数据的传递也是地址拷贝;有从缓冲区 buf 地址拷贝数据到接收者 receiver 栈内存地址,也有从发送者 sender 栈内存地址拷贝数据到缓冲区 buf;
- Channel 里面参数的修改不是并发安全的,包括对三个队列及其他参数的访问,因此需要加锁,本质上,channel 就是一个有锁队列;
- Channel 的性能跟 sync.Mutex 差不多,没有谁比谁强。Go 官方之所以推荐使用 Channel 进行并发协程的数据交互,是因为 channel 的设计理念能让程序变得简单,在大型程序、高并发复杂的运行状况中也是如此。
Happens Before
- 修改由多个 goroutines 同时访问的数据的程序必须串行化这些访问。
- 为了实现串行访问, 需要使用 channel 操作或其他同步原语(如 sync 和 sync/atomic 包中的原语)来保护数据。
- go 语句创建一个 goroutine,一定发生在 goroutine 执行之前。
- 往一个 channel 中发送数据,一定发生在从这个 channel 读取这个数据完成之前。
- 一个 channel 的关闭,一定发生在从这个 channel 读取到零值数据(这里指因为 close 而返回的零值数据)之前。
- 从一个无缓冲 channel 的读取数据,一定发生在往这个 channel 发送数据完成之前。