当前位置: 首页 > 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.dtcms.com/a/307855.html

相关文章:

  • tensorflow目标分类:分绍(一)
  • 力扣面试150题--只出现一次的数字II
  • ctfshow-web入门-254-266-反序列化
  • 人工智能之数学基础:频率和概率之间的关系
  • react调用接口渲染数据时,这些表格里的数据是被禁选的
  • 运行图生视频/文生视频(Wan2.X等)的显卡配置总结
  • 如何在 Ubuntu 24.04 或 22.04 LTS Linux 上安装 DaVinci Resolve
  • 图论-最短路 Bellman-Ford算法
  • 8.2-使用字符串存储 UTF-8 编码文本
  • AI对话-SpringAI破局尝试
  • TensorFlow深度学习实战——DeepDream
  • 分布式锁的基本原理和基于lua脚本的实现(Redisson)
  • 异常检测:算法分类及经典模型概览
  • 3.Linux 系统文件类型与文件权限
  • 学习笔记《区块链技术与应用》第三天 网络 难度
  • 力扣 Pandas 挑战(5)---数据分组
  • RabbitMQ 消费者确认 (Ack/Nack) (With Spring Boot)
  • Linux多线程线程控制
  • 专题:2025半导体行业研究报告:从AI芯片到封测突围的生死局|附40+份报告PDF、数据汇总下载
  • virtualbox+UBuntu20.04+内存磁盘扩容
  • Java异常:java.util.NoSuchElementException
  • 【数据结构初阶】--二叉树(六)
  • 学习笔记《区块链技术与应用》第4天 比特币脚本语言
  • window中qemu使用(安装ubuntu系统)
  • 嵌入式系统常用架构
  • python中各种装饰器的作用
  • 图像处理中级篇 [2]—— 外观检查 / 伤痕模式的原理与优化设置方法
  • 【n8n教程笔记——工作流Workflow】文本课程(第二阶段)——1 理解数据结构 (Understanding the data structure)
  • 【单片机】【分布式】从单机到分布式:Redis如何成为架构升级的关键力量
  • uniapp开发App如何使用正确高德地图。uni.chooseLocation数据不展示问题,uni.getLocation不生效问题。