前言

今天发现了一个问题是之前一直没有注意到的,这里记一下

正文

Send Closed Chan

问题概述

代码逻辑是启动时启动多个 channel, channel1 获取数据监听数据处理后发送给 channel2 , channel2 处理后再给 channel3 等等

在 channel1 写完数据后将通道 channel1 关闭, channel1 关闭后 channel2 也关闭, 达到任务执行完毕后通道全部关闭的效果

我之前的代码是

func StartVerify() {
wg := sync.WaitGroup{}
for {
data, ok := <-TypicalResChan
if !ok {
wg.Wait()
close(VerifyBossDNSChan)
break
}
go func() {
wg.Add(1)
ok := Verify(data)
if ok {
VerifyBossDNSChan <- data
}
wg.Done()
}()
}
}

后来使用中发现有时候会报 send closed channel 的错误,大佬看了一眼就发现了问题

问题剖析

就以上面的举例, 上游向 TypicalResChan 发送数据时, 如果不是 close 请求, 会启动一个 goroutine 去处理逻辑, 而在 启动这个 goroutine 后在内部进行 wg 的 Add 注册, 注意这个过程是有耗时的, 所以问题来了, 当上游向 TypicalResChan 发送 close 时, 进入 !ok 的逻辑, 此时等待 wg 释放, 此时有可能上一个数据接收到后还在启动一个 goroutine ,还没有 Add注册, 此时wg没有监听到这个 goroutine 的注册, 造成不会等待这个 goroutine ,直接就关闭了 TypicalResChan, 而这个 goroutine 执行后向 TypicalResChan 发送数据时 TypicalResChan 已经关闭, 所以会报错导致 panic

另外, 还需要注意的是在 wg 没有注册前就 wait 是不推荐的, 很容易出现问题

还有就是判断通道关闭更推荐使用 range 省去判断 !ok 的步骤保持代码整洁

问题解决

修改成这样即可

func StartVerify() {
wg := sync.WaitGroup{}
for data := range TypicalResChan {
wg.Add(1)
go func(data Result) {
defer wg.Done()
ok := Verify(data)
if ok {
VerifyBossDNSChan <- data
}
}(data)
}
wg.Wait()
close(VerifyBossDNSChan)
}

在上游 close 时 range 会自动结束, 而受到正常数据先 Add 防止时间差导致的 Add 失败问题, 在 1.14 后 go 优化了 defer 的逻辑, defer 基本不再有消耗, 所以推荐使用 defer 注册 wg 的关闭, 而在 close 时, for 循环结束, wg 在 wait 后再 close

最新文章

  1. espcms内容页上下篇按后台手动排序号
  2. 吃透Javascript数组操作的正确姿势—再读《Js高程》
  3. Statement和PreparedStatement的区别; 什么是SQL注入,怎么防止SQL注入?
  4. Linux:kill 进程
  5. 对table的tr使用display:block显示colspan失效问题的解决
  6. 集成学习(Ensembling Learning)
  7. javascript设计模式-生成器模式(Builder)
  8. Matlab之cell使用
  9. Demo学习: ClientEvents
  10. 获取元素位置信息:getBoundingClientRect
  11. 工厂方法模式(java 设计模式)
  12. 最短路(dijskra+SPFA+Bellman)
  13. 编译SASS
  14. rndc 错误解决 和 远程配置
  15. Unity3D项目优化(转)
  16. ActiveMQ学习系列(四)----消息持久化到mysql
  17. P1451 求细胞数量
  18. 【macOS】 在OpenCV下训练Haar特征分类器
  19. java基础知识总结--对象的克隆
  20. 查询部门----返回给前台TreeView数据格式的数据

热门文章

  1. kubernetes 中的证书工作机制
  2. 团队作业part2--需求规格说明书
  3. ASP数据库连接方法语法总结
  4. Filebeat+Logstash自定义多索引
  5. Fastjson 1.2.47 远程命令执行漏洞复现
  6. 一个java文件被执行的历程
  7. db2密码中有感叹号时,连接方法
  8. get \post 接口代码及断言编写
  9. 全栈工程师-史上最强VSCODE插件-提高开发效率
  10. VSCode + PYQT5 搭建图形化界面