• 搜索
  • 夜间模式
    ©2012-2026  陈十一的小破站 Theme by OneBlog

    陈十一的小破站博客

    搜索
    标签
    # Nodejs # CentOS # Git # Golang # Docker # Windows # Nginx # 反向代理 # 脚本 # Linux
  • 首页>
  • 技术>
  • 正文
  • Go并发同步:WaitGroup与Channel详解

    2025年06月02日 716 阅读 0 评论 2889 字

    在 Go 并发编程中,sync.WaitGroup 和 Channel 是两大核心同步机制。它们分别适用于不同场景,复杂场景下常需组合使用。本文将深入探讨两者的使用模式、常见陷阱及组合实践。

    1. WaitGroup:等待协程组完成

    WaitGroup 用于等待一组 goroutine 完成执行,是最基础的同步原语。

    基础使用模式

    func main() {
        var wg sync.WaitGroup
        for i := 1; i <= 5; i++ {
            wg.Add(1)    // 1. 预增加计数
            i := i       // 避免闭包捕获同一个变量
            go func() {
                defer wg.Done()  // 3. 任务完成时递减计数
                fmt.Printf("Worker %d starting\n", i)
            }()
        }
        wg.Wait()  // 4. 阻塞等待所有任务完成
    }

    关键注意事项

    1. Add 的位置:必须在启动 goroutine 前调用 Add()
    2. 变量捕获:循环中使用局部变量避免闭包捕获同一变量
    3. Done 调用:推荐使用 defer wg.Done() 确保执行

    常见错误及解决方案

    错误类型现象解决方案
    Add 在 goroutine 内提前触发 Wait()确保在启动 goroutine 前调用 Add()
    WaitGroup 值复制死锁使用指针传递(*sync.WaitGroup)
    忘记启动 goroutine编译错误检查 go func() 语法
    多次 Wait()逻辑混乱保持单 Wait() 调用点

    2. Channel:协程间通信管道

    Channel 是类型安全的通信管道,支持同步和异步操作。

    基础通信模式

    func main() {
        ch := make(chan string)  // 无缓冲通道
        go func() { ch <- "ping" }()
        msg := <-ch  // 同步阻塞接收
        fmt.Println(msg)
    }

    核心特性解析

    特性说明示例
    缓冲通道异步非阻塞通信make(chan int, 3)
    通道关闭通知接收方结束close(ch)
    方向限定限制通道操作func worker(ch chan<- int)
    Range 遍历自动检测关闭for v := range ch { ... }

    通道关闭的黄金法则

    1. 发送方负责关闭:避免向已关闭通道发送数据
    2. 接收安全:关闭后可继续接收剩余数据
    3. 零值机制:关闭后接收返回零值

      v, ok := <-ch  // ok=false 表示通道已关闭

    3. WaitGroup 与 Channel 组合使用

    复杂场景常需组合两者实现高效并发控制。

    典型死锁案例

    // 错误示例:无缓冲通道导致死锁
    func main() {
        ch := make(chan int)
        var wg sync.WaitGroup
        
        for i := 0; i < 2; i++ {
            wg.Add(1)
            go func() {
                ch <- 1  // 阻塞等待接收方
                wg.Done()
            }()
        }
        
        wg.Wait()      // 永远阻塞在此
        close(ch)      // 无法执行到此处
        
        for v := range ch { ... }
    }

    正确解决方案

    func main() {
        ch := make(chan int)
        var wg sync.WaitGroup
        
        // 生产者
        for i := 0; i < 2; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                ch <- 1
            }()
        }
        
        // 独立协程处理关闭
        go func() {
            wg.Wait()
            close(ch)  // 确保所有发送完成后再关闭
        }()
        
        // 消费者
        for v := range ch {
            fmt.Println(v)
        }
    }

    生产级最佳实践

    1. 缓冲通道优化:根据任务量设置合理缓冲区

      results := make(chan Result, taskCount)
    2. 多通道管理:结合 select 处理多种消息类型
    3. 超时控制:搭配 context 实现超时取消
    4. 错误传递:专用 error 通道收集错误

    总结对比

    特性WaitGroupChannel
    主要用途协程组完成等待协程间通信
    同步方式计数屏障数据传递
    适用场景并行任务组生产者-消费者
    组合优势简单任务同步复杂数据流控制

    黄金法则:

    • 纯同步需求 → WaitGroup
    • 数据传递需求 → Channel
    • 复杂工作流 → WaitGroup + Channel + Context
    本文著作权归作者 [ 陈十一 ] 享有,未经作者书面授权,禁止转载,封面图片来源于 [ 互联网 ] ,本文仅供个人学习、研究和欣赏使用。如有异议,请联系博主及时处理。
    GolangWaitGroupChannel
    — END —
    Copyright©2012-2026  All Rights Reserved.  Load:0.013 s
    Theme by OneBlog V3.6.5
    夜间模式

    开源不易,请尊重作者版权,保留基本的版权信息。