当前位置: 首页 > news >正文

go goroutine chan 用法

方法1

代码

package mainimport ("fmt""sync""time"
)func main() {allChan := make(chan interface{}, 3)var sendWg, recvWg sync.WaitGroup // 分别同步发送和接收// 发送goroutinesendWg.Add(1)go func() {defer sendWg.Done()for i := 0; i < 10; i++ {time.Sleep(time.Millisecond * 10)allChan <- ifmt.Printf("发送: %d\n", i)}close(allChan) // 发送完成后关闭channel}()// 接收goroutinerecvWg.Add(1)go func() {defer recvWg.Done()for item := range allChan {time.Sleep(time.Millisecond * 20) // 模拟处理耗时fmt.Printf("接收: %v\n", item)}}()// 先等待发送完成,再等待接收完成sendWg.Wait()recvWg.Wait()fmt.Println("所有操作完成")
}

运行结果:

标准输出:发送: 0
发送: 1
接收: 0
发送: 2
发送: 3
接收: 1
发送: 4
发送: 5
接收: 2
发送: 6
接收: 3
发送: 7
接收: 4
发送: 8
接收: 5
发送: 9
接收: 6
接收: 7
接收: 8
接收: 9
所有操作完成

分析

优化说明

  1. 双重同步:
  • 使用两个WaitGroup(sendWg和recvWg),分别等待发送和接收 goroutine 完成。
  • 先通过sendWg.Wait()确保所有数据发送完毕并关闭 channel;
  • 再通过recvWg.Wait()确保接收 goroutine 处理完所有数据。
  1. channel 关闭逻辑:
  • 发送完成后关闭 channel 是关键,这会让接收 goroutine 的range循环在取完数据后自动退出,避免接收端阻塞。
  1. 适用场景:
  • 这种 “发送 + 接收都异步” 的模式适合需要主程序同时处理其他逻辑的场景(例如同时监控多个任务),但必须做好同步,否则会出现数据丢失。

方法2

代码

package mainimport ("fmt""time"
)func main() {// 1. 数据通道:传递实际数据dataChan := make(chan interface{}, 3)// 2. 发送完成信号通道:发送方结束后通知sendDone := make(chan struct{})// 3. 接收完成信号通道:接收方结束后通知recvDone := make(chan struct{})// 发送goroutinego func() {for i := 0; i < 10; i++ {time.Sleep(time.Millisecond * 10)dataChan <- ifmt.Printf("发送: %d\n", i)}close(dataChan)       // 发送完毕,关闭数据通道(通知接收方结束)sendDone <- struct{}{} // 发送完成信号}()// 接收goroutinego func() {for item := range dataChan { // 当dataChan关闭且数据取完后,循环自动退出time.Sleep(time.Millisecond * 20)fmt.Printf("接收: %v\n", item)}recvDone <- struct{}{} // 接收完成信号}()// 主程序等待:先等发送完成,再等接收完成<-sendDone  // 阻塞等待发送完成<-recvDone  // 阻塞等待接收完成fmt.Println("所有操作完成")
}

运行结果

标准输出:发送: 0
发送: 1
接收: 0
发送: 2
发送: 3
接收: 1
发送: 4
发送: 5
接收: 2
发送: 6
接收: 3
发送: 7
接收: 4
发送: 8
接收: 5
发送: 9
接收: 6
接收: 7
接收: 8
接收: 9
所有操作完成

那种更好呢

一、效率对比

sync.WaitGroup 略占优势

  1. sync.WaitGroup 是 Go 标准库专门为 “等待一组 goroutine 完成” 设计的同步原语,底层通过原子操作(计数器加减)实现,几乎没有额外开销,执行效率极高。
  2. 它的逻辑简单:Add 增加计数、Done 减少计数、Wait 阻塞直到计数归零,整个过程不涉及复杂的 runtime 调度交互。
    多通道同步的开销稍高
  3. 通道同步依赖于 channel 的发送 / 接收操作,这些操作需要与 Go runtime 的调度器交互(如检查通道状态、唤醒阻塞的 goroutine 等),理论上比原子操作多一些微小的开销。
  4. 额外的信号通道(如 sendDone、recvDone)会占用少量内存(每个通道需要维护内部数据结构),但在实际应用中影响可忽略。

二、实用性与适用场景

sync.WaitGroup 更适合大多数场景

  1. 代码简洁:对于 “等待多个 goroutine 完成” 的场景,WaitGroup 的语义更直观(Add/Done/Wait 直接对应 “注册 - 完成 - 等待” 逻辑),可读性更高。
  2. 扩展性好:如果需要等待多个发送者或接收者(如 10 个发送 goroutine),WaitGroup 只需一次 Add(10) 即可,无需额外定义多个信号通道。
  3. 通用性强:是 Go 社区的 “标准做法”,团队协作时更容易被理解。
    多通道同步适合特定场景
  4. 符合 Go 哲学:更贴合 “用通信实现共享内存” 的 Go 设计思想,通过 channel 传递信号比直接操作计数器更 “Go 式”。
  5. 灵活扩展:如果需要更复杂的同步逻辑(如 “先等待 A 完成,再启动 B,最后等待 B 完成”),通道的阻塞特性可以天然实现流程控制,而 WaitGroup 可能需要配合额外逻辑。
  6. 无依赖:不依赖 sync 包,纯靠语言原生特性实现,适合理解 channel 机制的场景。

三、总结建议

优先选 sync.WaitGroup:在大多数业务代码中,它更简洁、高效、易维护,是同步 goroutine 的 “标准答案”。
选多通道同步:当你需要强调 “通信优先” 的设计,或同步逻辑较复杂(需要通过通道传递更多状态)时,它是更优雅的选择。

本质区别:

WaitGroup 是 “共享状态” 式同步(通过计数器),多通道是 “通信” 式同步(通过信号传递)。两者效率差异微小,选择时主要看代码可读性和场景适配性。

http://www.lryc.cn/news/605962.html

相关文章:

  • 网络编程(一)TCP编程和UDP编程
  • 前端工程化包管理器:从npm基础到nvm多版本管理实战
  • Vue多请求并行处理实战指南
  • Acrel-1000系列分布式光伏监控系统在湖北荆门一马光彩大市场屋顶光伏发电项目中应用
  • 【人工智能-15】OpenCV直方图均衡化,模板匹配,霍夫变换,图像亮度变换,形态学变换
  • webpack-babel
  • ESXI虚拟交换机 + H3C S5120交换机 + GR5200路由器组网笔记
  • 如何将照片从 realme 手机传输到电脑?
  • 使用橙武低代码平台构建摄影店管理系统的完整指南
  • 【爬虫实战】使用Python和JS逆向基于webpack的游戏平台
  • Rust × WebAssembly 项目脚手架详解
  • Kubernetes 应用部署实战:为什么需要 Kubernetes?
  • 本土发货模式兴起,如何选择合适的海外仓系统?
  • 单张卡牌类
  • 星云能量传送特效技术详解
  • Servlet修改新增思路
  • C语言---结构体(格式、用法、嵌套、初始化)、共用体、枚举类型、typedef类型
  • 舱驾操作系统架构规划
  • 使用的IDE没有内置MCP客户端怎么办?
  • AI 类型的 IDE
  • AI IDE+AI 辅助编程-生成的大纲-一般般
  • 掩码语言模型(MLM)技术解析:理论基础、演进脉络与应用创新
  • 从循环依赖谈 Chromium 模块化设计:编译结构与最佳实践
  • 基于 Amazon Nova Sonic 和 MCP 构建语音交互 Agent
  • 开发避坑短篇(11):Oracle DATE(7)到MySQL时间类型精度冲突解决方案
  • USRP捕获手机/路由器数据传输信号波形(下)
  • 6.苹果ios逆向-过ssl证书检测-安装SSL Kill Switch 3
  • JVM字节码文件结构剖析
  • uniapp Vue3版本使用pinia存储持久化插件pinia-plugin-persistedstate对微信小程序的配置
  • 【生活篇】Ubuntu22.04安装网易云客户端