Go语言并发编程中 WaitGroup 实现原理

发布于 1周前 作者 zlyuanteng 最后一次编辑是 5天前 来自 问答

Go语言并发编程中 WaitGroup 实现原理

WaitGroup 主要维护了 2 个计数器,一个是请求计数器 v,一个是等待计数 器 w,二者组成一个64bit 的值,请求计数器占高 32bit,等待计数器占低 32bit。

每次 Add 执行,请求计数器 v 加 1,Done 方法执行,等待计数器减 1,v 为0 时通过信号量唤醒 Wait()。

1 回复

Go语言并发编程中 WaitGroup 实现原理

在Go语言的并发编程中,sync.WaitGroup 是一个非常实用的同步原语,用于等待一组协程(goroutines)的完成。它允许主协程阻塞,直到所有的子协程都执行完成。WaitGroup 内部维护一个计数器,每启动一个协程就调用 Add(1) 来增加计数,协程结束时调用 Done()(实际上是 Add(-1))来减少计数。当计数达到零时,所有在 Wait() 上等待的协程都将被唤醒。

实现原理概述

  • 内部计数器WaitGroup 内部使用一个计数器来跟踪待完成的协程数量。
  • 状态存储WaitGroup 使用一种高效的方式来存储和修改这个计数器及其状态,以避免在并发环境下的竞态条件。
  • 等待队列:当调用 Wait() 时,如果计数器不为零,调用协程会被阻塞并放入一个等待队列中。
  • 唤醒机制:每当计数器减到零时,所有在等待队列中的协程都会被唤醒。

示例代码

下面是一个使用 sync.WaitGroup 的简单示例,展示了如何等待多个协程完成:

package main

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

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 确保协程结束时调用Done()
    fmt.Printf("Worker %d starting\n", id)

    time.Sleep(time.Second) // 模拟耗时操作
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1) // 增加计数器
        go worker(i, &wg) // 启动协程
    }

    wg.Wait() // 等待所有协程完成
    fmt.Println("All workers finished")
}

注意事项

  • 使用 WaitGroup 时,务必确保每个 Add(1) 调用都有对应的 Done()Add(-1) 调用,以避免死锁或提前唤醒。
  • WaitGroup 不能被复制,因此应该通过指针在多个协程间共享。
  • 在复杂的并发场景中,可能需要结合使用 WaitGroup 与其他同步机制(如互斥锁、条件变量等)来确保程序的正确性和效率。
回到顶部