返回

Go并发之 WaitGroup详解:深入剖析底层原理与源码实现

后端

WaitGroup:同步 Go 程的并发机制

什么是 WaitGroup?

WaitGroup 是一种 Go 语言包,提供了 Wait 方法,该方法可以阻塞当前 Goroutine,直到 WaitGroup 计数器减为 0。通过 WaitGroup,我们可以轻松同步多个 Goroutine 的执行。

WaitGroup 的工作原理

WaitGroup 的底层实现包含一个计数器和一个锁。当调用 Add 方法时,锁会被加锁,计数器增加 1,然后锁会被解锁。当调用 Done 方法时,锁会被加锁,计数器减少 1,然后锁会被解锁。当 Wait 方法被调用时,锁会被加锁,然后检查计数器是否为 0。如果不为 0,则锁会被解锁,当前 Goroutine 会被阻塞。如果计数器为 0,则锁会被解锁,当前 Goroutine 会继续执行。

type WaitGroup struct {
    state int64
}

func (wg *WaitGroup) Add(delta int) {
    atomic.AddInt64(&wg.state, int64(delta))
}

func (wg *WaitGroup) Done() {
    atomic.AddInt64(&wg.state, -1)
}

func (wg *WaitGroup) Wait() {
    for {
        v := atomic.LoadInt64(&wg.state)
        if v <= 0 {
            break
        }
        runtime.Gosched()
    }
}

WaitGroup 的使用场景

WaitGroup 可以用于各种场景,包括:

  • 等待多个 Goroutine 执行完成
  • 控制并发请求的数量
  • 实现生产者-消费者模式
  • 实现管道通信

示例:等待多个 Goroutine 完成

例如,我们有一个函数需要等待 10 个 Goroutine 执行完成,我们可以使用 WaitGroup 如下所示:

var wg WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        // 执行耗时的任务
    }(i)
}
wg.Wait()

结论

WaitGroup 是一个非常重要的并发控制机制,它可以帮助我们轻松地同步执行多个 Goroutine。WaitGroup 的底层实现非常简单,但它却非常高效。我们可以使用 WaitGroup 在各种场景中实现并发编程。

常见问题解答

  1. WaitGroup 和 sync.Mutex 有什么区别?
    WaitGroup 用于同步 Goroutine 的执行,而 sync.Mutex 用于保护共享资源的访问。

  2. WaitGroup 和通道有什么区别?
    WaitGroup 是一种阻塞机制,而通道是一种非阻塞机制。WaitGroup 用于等待所有 Goroutine 完成,而通道用于在 Goroutine 之间传递数据。

  3. 我可以在一个 Goroutine 中使用多个 WaitGroup 吗?
    是的,你可以在一个 Goroutine 中使用多个 WaitGroup。每个 WaitGroup 都会独立地跟踪自己的计数器。

  4. WaitGroup 可以用于哪些并发模式?
    WaitGroup 可以用于各种并发模式,包括生产者-消费者模式和管道通信模式。

  5. WaitGroup 的性能怎么样?
    WaitGroup 的性能非常高效,因为它使用原子操作来管理计数器。