Go channel
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情 >>
1. 基础
channel是什么:通道可以使一个goroutine发送特定值到另一个goroutine的通信机制。通道相当与一个先进先出(FIFO)的队列,使用map数据结构实现。channel 中包括 buffer、sendx 和 recvx 收发的位置(ring buffer 记录实现)、sendq、 recv。无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的。
Channel的操作符是箭头<-,箭头指向是数据的流向
channel必须先创建再使用,make(chan int)
channel 中使用了 ring buffer(环形缓冲区) 来缓存写入的数据。recvx 指向最早被读取的数 据,sendx 指向再次写入时插入的位置。当 channel 因为缓冲区不足而阻塞了队列,则使用双向链表存储。
2.基本操作
应用场景:
channel适合运用在数据流动的地方,数据在多个协程中流动,比如说
- 任务定时:超时处理/定时任务
- 解耦生产者和消费者
- 控制并发数,并发控制
- 事件订阅和广播
- 同步与异步
常用操作:
- 使用for range读channel
- 使用_ , ok判断channel是否关闭,ok 表示接收到的值是否是在 channel 被关闭前发送的。true表示读到数据,通道没有关闭;false 表示通道关闭,读不到数据
- select 处理多个channel
3. 注意事项
3.1 和锁的区别:
-
channel关注的是并发问题的数据流动,适用于数据在多个协程中流动的场景。
- 传递数据的所有权,即把某个数据发送给其他协程
- 分发任务,每个任务都是一个数据
- 交流异步结果,结果是一个数据
-
而mutex关注的是是数据不动,某段时间只给一个协程访问数据的权限,适用于数据位置固定的场景。
- 缓存
- 状态
3.2 channel使用
-
向为nil的channel发送数据,会造成永远阻塞
- 空通道=无缓冲通道,channel中的发送操作将会被阻塞,直到另一个goroutine在对应的通道执行接受操作。
- 如果从一个 nil 的 channel 中接收数据,也会造成永久阻塞。
- 给一个已经关闭的 channel 发送数据, 会引起 panic
- 从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值。
同一个协程中,对无缓冲的channel同时发送和接受数据
- 同一个协程里,不能对无缓冲channel同时发送和接收数据,如果这么做会直接报错死锁。
- 对于一个无缓冲的channel而言,只有不同的协程之间一方发送数据一方接受数据才不会阻塞。channel无缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据。