Go 协程同步工具 WaitGroup 使用全解
2024-02-14 11:40:44
前言
WaitGroup 是 Go 语言中常用的协程同步工具,它允许主协程等待一组协程完成,才继续执行下一步任务。WaitGroup 的源码也较为简单,那不妨通过业务推导方式,自己梳理出实现逻辑,这样以后就靠推导而无需记忆实现原理了。
情景分析
假设我们有一个场景:我们要在一个网站上并行处理多个请求,每个请求都需要从数据库中获取数据,然后将数据返回给客户端。为了提高并发性能,我们使用 Go 语言的协程来处理这些请求。
我们可以创建一个主协程来等待所有协程完成,然后再继续执行下一步任务。如果我们不使用 WaitGroup,那么主协程将无法知道何时所有协程都已完成,从而导致主协程可能在某些协程尚未完成时就开始执行下一步任务,这可能会导致数据不一致或其他问题。
WaitGroup 的使用
WaitGroup 有两个主要方法:Add()
和 Wait()
。Add()
方法用于增加等待协程的数量,Wait()
方法用于等待所有协程完成。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
// 增加需要等待的协程数量
wg.Add(2)
// 创建两个协程
go func() {
// 模拟协程执行任务
fmt.Println("协程 1 开始执行")
time.Sleep(time.Second * 2)
fmt.Println("协程 1 执行完成")
// 协程执行完成,减少等待协程的数量
wg.Done()
}()
go func() {
// 模拟协程执行任务
fmt.Println("协程 2 开始执行")
time.Sleep(time.Second * 3)
fmt.Println("协程 2 执行完成")
// 协程执行完成,减少等待协程的数量
wg.Done()
}()
// 等待所有协程完成
wg.Wait()
// 所有协程执行完成,主协程继续执行
fmt.Println("所有协程执行完成")
}
在这个示例中,我们首先创建了一个 WaitGroup 变量 wg
。然后,我们使用 wg.Add()
方法增加了两个需要等待的协程数量。接下来,我们创建了两个协程,每个协程都模拟执行了一个任务,然后使用 wg.Done()
方法减少了等待协程的数量。最后,我们使用 wg.Wait()
方法等待所有协程完成,然后主协程继续执行。
WaitGroup 的实现原理
WaitGroup 的实现原理非常简单,它使用了一个原子计数器来记录等待协程的数量。当一个协程完成时,它会调用 wg.Done()
方法,原子计数器就会减少 1。当原子计数器变为 0 时,说明所有协程都已完成,wg.Wait()
方法就会返回。
// WaitGroup 的结构体定义
type WaitGroup struct {
state int32
}
// Add 方法用于增加等待协程的数量
func (wg *WaitGroup) Add(delta int) {
atomic.AddInt32(&wg.state, int32(delta))
}
// Done 方法用于减少等待协程的数量
func (wg *WaitGroup) Done() {
atomic.AddInt32(&wg.state, -1)
}
// Wait 方法用于等待所有协程完成
func (wg *WaitGroup) Wait() {
for atomic.LoadInt32(&wg.state) != 0 {
runtime.Gosched()
}
}
WaitGroup 的使用场景
WaitGroup 可以用于多种场景,包括:
- 等待一组协程完成,才继续执行下一步任务。
- 限制并发协程的数量。
- 在多个协程之间共享数据。
WaitGroup 是一个非常有用的工具,它可以帮助我们编写出更可靠和高效的并发程序。
总结
WaitGroup 是 Go 语言中常用的协程同步工具,它允许主协程等待一组协程完成,才继续执行下一步任务。WaitGroup 的使用非常简单,只需要使用 wg.Add()
和 wg.Wait()
这两个方法即可。WaitGroup 的实现原理也很简单,它使用了一个原子计数器来记录等待协程的数量。WaitGroup 可以用于多种场景,包括等待一组协程完成、限制并发协程的数量以及在多个协程之间共享数据。