五、从channel接收数据1 接收数据的格式
1) 阻塞接收数据
程序阻塞直到收到数据并赋值
data := <-ch
2) 非阻塞接收数据
非阻塞的通道接收方法可能造成高的 CPU 占用
//ok表示是否接收到数据data, ok := <-ch
3) 接收数据并忽略
程序阻塞直到接收到数据,但接收到的数据会被忽略
<-ch
4) 循环接收
channel是可以进行遍历的,遍历的结果就是接收到的数据
for data := range ch {//done}5) SELECT语句接收
select 的特点是只要其中有一个 case 已经完成,程序就会继续往下执行,而不会考虑其他 case 的情况在一个 select 语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语如果其中的多条case语句可继续执行(即没有被阻塞),那么就从这些case语句中任意选择一条如果没有case语句可以执行(即所有的通道都被阻塞):1) 如果有 default 语句,执行 default 语句,同时程序的执行会从 select 语句后的语句中恢复2) 如果没有 default 语句,那么 select 语句将被阻塞,直到至少有一个case可以进行下去
select {case <- chan1://donecase chan2 <- 2://donedefault://done}
2 读取数据的流程
1) 流程图如下:

文章插图
其中:
G表示一个goroutine虚线表示recvq中堵塞的G被唤醒的流程,如果G没有被唤醒,则一直堵塞下去,此时关闭channel,会得到channel类型的零值2) 过程描述:
1 如果等待发送队列sendq不为空,且没有缓冲区,直接从sendq中取出G,读取数据,最后把G唤醒,结束读取过程2 如果等待发送队列sendq不为空,有缓冲区(此时缓冲区满了),从缓冲区中首部读出数据,把sendq出列的G中数据写入缓冲区尾部,把G唤醒,结束读取过程3 如果等待发送队列sendq为空,且环形队列无元素,将goruntime加入等待接收队列recvq中进行堵塞,等待被唤醒4 如果等待发送队列sendq为空,环形队列有元素,直接从缓冲区读取数据,结束读取过程六、关闭channel1 格式
close(ch)2 过程描述
1) 首先校验chan是否已被初始化,然后加锁之后再校验是否已被关闭过,如果校验都通过了,那么将closed字段设值为12) 遍历recvq和sendq,并将所有的goroutine 加入到glist中3) 将所有glist中的goroutine加入调度队列,等待被唤醒4) recvq中的goroutine接收到对应数据的零值,sendq中的goroutine会直接panic七、channel发送、接收数据过程可能产生的问题1 向一个nil的channel发送/读取数据会一直堵塞下去?该如何唤醒?
会一直堵塞下去,不会被唤醒,可能会造成泄露,这是一个BUG
2 等待发送队列(sendq)中有数据,如果一直没有gouruntine从channel里面读数据会不会造成泄漏?
会造成泄露,channel用完了,最好要close
3 向已经关闭的channel读/写数据会发生什么?
写已经关闭的 channel 会触发panic
读已经关闭的 channel,能一直读到数据:
1) 如果 channel 关闭前,buf内有元素还未读,会正确读到 channel 内的值,且返回的第二个 bool 值为 true
2) 如果 channel 关闭前,buf内有元素已经被读完,channel 内无值,返回 channel 元素的零值,第二个 bool 值为 false
4 触发 panic 的三种情况
1) 向一个关闭的 channel 进行写操作
2) 关闭一个为 nil 的 channel
3) 重复关闭一个 channel
【golang channel底层结构和实现】
经验总结扩展阅读
- 浅谈 Golang 插件机制
- 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
- 深入底层C源码 Redis核心设计原理
- 补充部分---ScheduledThreadPoolExecutor类分析 线程池底层原理详解与源码分析
- 用golang开发系统软件的一些细节
- 密码学奇妙之旅、02 混合加密系统、AES、RSA标准、Golang代码
- HashMap底层原理及jdk1.8源码解读
- 密码学奇妙之旅、03 HMAC单向散列消息认证码、Golang代码
- 高启强翻烂的《孙子兵法》,藏着普通人恋爱7个底层逻辑
- 《再见爱人》|宋宁峰与张婉婷相爱相杀的底层逻辑