channel底层结构
channel的类型
- 有缓冲。创建的时候不指定大小,常被当做信号signal来使用
- 无缓冲
channel的几种操作模式
| 写操作模式 | 读操作模式 | 读写操作模式 |
---|
创建 | make(chan<-int) | make(<-chan int) | make(chan int) |
channel的几种状态:未初始化,正常,关闭
| 未初始化 | 关闭 | 正常 |
---|
关闭 | panic | panic | 正常关闭 |
发送 | 永久阻塞死锁 | panic | 阻塞或成功发送 |
接收 | 永久阻塞死锁 | 缓冲区为空则为零值,否则可以正常读取 | 阻塞或成功接收 |
注意:
1. 一个channel不能多次关闭,会导致panic
2. 如果多个goroutine都监听同一个channel,那么channel上的数据会被先到的goroutine取走。
3. 如果多个goroutine都监听同一个channel,当这个channel退出时,所有的监听goroutine都能收到退出信号
向channel发送数据时大概分为两步:检查和发送
- 如果channel的读等待队列存在接收者goroutine
将数据直接发送给第一个等待的goroutine,唤醒下一个接收的goroutine - 如果channel的读等待队列不存在接收者goroutine
- 如果循环数组buf未满,那么把待发送数据放到数组队尾
- 如果buf已满,这个时候就会走阻塞发送流程,将当前goroutine加入写等待队列,并挂起等待唤醒
从channel接收数据时大概分为两步:检查和数据发送
- 如果channel的写等待队列存在发送者goroutine
- 如果是无缓冲channel,直接从第一个发送者goroutine那里把数据拷贝给接收变量,唤醒发送的goroutne
- 如果是有缓冲channel(已满),将循环数组buf的队首元素拷贝给接收变量,将第一个发送者goroutine的数据拷贝到buf循环数组队尾,唤醒发送的goroutine
- 如果channel的写等待队列不存在发送者goroutine
- 如果循环数组buf非空,将循环数组buf的队首元素拷贝给接收变量
- 如果循环数组buf为空,这个时候就会走阻塞接收的流程,将当前goroutine加入读等待队列,并挂起等待唤醒