侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

如何创建一个Goroutine并等待其完成?

2025-12-12 / 0 评论 / 4 阅读

题目

如何创建一个Goroutine并等待其完成?

信息

  • 类型:问答
  • 难度:⭐

考点

Goroutine创建, sync.WaitGroup使用, 并发控制

快速回答

使用Go关键字创建Goroutine,配合sync.WaitGroup实现同步:

  1. 声明sync.WaitGroup变量
  2. 启动Goroutine前调用wg.Add(1)
  3. 在Goroutine内部使用defer wg.Done()
  4. 主函数中调用wg.Wait()等待结束
## 解析

原理说明

Goroutine是Go语言的轻量级线程,由Go运行时管理。使用sync.WaitGroup可解决主协程提前退出导致子协程未完成的问题,它通过计数器实现协程同步:

  • Add(n):增加计数器值
  • Done():计数器减1(等价于Add(-1)
  • Wait():阻塞直到计数器归零

代码示例

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1) // 计数器+1
        go worker(i, &wg) // 注意传递指针
    }

    wg.Wait() // 等待所有worker完成
    fmt.Println("所有任务完成")
}

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 确保函数退出时执行

    fmt.Printf("Worker %d 开始工作\n", id)
    time.Sleep(time.Second) // 模拟耗时任务
    fmt.Printf("Worker %d 完成工作\n", id)
}

最佳实践

  • 传递WaitGroup指针:避免值拷贝导致计数器失效
  • 使用defer调用Done():确保即使发生panic也能释放计数器
  • Add在Goroutine外调用:防止竞态条件
  • 控制并发数:避免无限制创建Goroutine导致资源耗尽

常见错误

  • 忘记Add:导致Wait()立即返回,协程未执行完
  • 传递值而非指针wg拷贝后计数器不共享
  • Done()调用次数不足Wait()永久阻塞
  • 在Goroutine内调用Add:可能主协程先执行Wait()

扩展知识

  • Goroutine泄漏:未正确退出会导致内存泄漏,可用runtime.NumGoroutine()检测
  • 替代方案errgroup.Group(支持错误传递)、context.Context(超时控制)
  • 调度机制:GMP模型(Goroutine-Machine-Processor)实现高效调度
  • 并发vs并行:Goroutine是并发单元,Go运行时自动利用多核实现并行