返回

Go 协程同步工具 WaitGroup 使用全解

后端




前言

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 可以用于多种场景,包括等待一组协程完成、限制并发协程的数量以及在多个协程之间共享数据。