当前位置:首页 > Go > 正文

Go语言并发编程实战(多个WaitGroup的使用详解)

Go语言并发编程中,sync.WaitGroup 是一个非常重要的同步原语,用于等待一组 goroutine 完成执行。很多初学者知道如何使用单个 WaitGroup,但在复杂场景下,可能需要同时管理多个 WaitGroup。本文将通过通俗易懂的方式,带你掌握多个WaitGroup的使用方法,即使是编程小白也能轻松上手!

Go语言并发编程实战(多个WaitGroup的使用详解) Go语言并发编程 WaitGroup使用教程 多个WaitGroup示例 Go并发同步机制 第1张

什么是 WaitGroup?

sync.WaitGroup 是 Go 标准库 sync 包中的一个结构体,用于协调多个 goroutine 的执行。它内部维护一个计数器:

  • Add(n):增加计数器
  • Done():减少计数器(通常在 goroutine 结束时调用)
  • Wait():阻塞当前 goroutine,直到计数器归零

为什么需要多个 WaitGroup?

在实际开发中,我们可能有不同类型的任务需要并行处理,比如:

  • 一部分任务负责读取数据
  • 另一部分任务负责写入数据
  • 还有一部分任务负责日志记录

如果我们只用一个 WaitGroup,就无法区分这些任务组的完成状态。而使用多个WaitGroup,我们可以分别等待每类任务完成,实现更精细的控制。

示例:使用两个 WaitGroup 分别管理读写任务

下面是一个完整的代码示例,演示如何使用两个 WaitGroup 来分别等待“读任务”和“写任务”的完成:

package mainimport (	"fmt"	"sync"	"time")func main() {	// 创建两个 WaitGroup	var readWg sync.WaitGroup	var writeWg sync.WaitGroup	// 启动3个读任务	readWg.Add(3)	for i := 0; i < 3; i++ {		go func(id int) {			defer readWg.Done()			fmt.Printf("读任务 %d 开始\n", id)			time.Sleep(time.Millisecond * 500) // 模拟耗时操作			fmt.Printf("读任务 %d 完成\n", id)		}(i)	}	// 启动2个写任务	writeWg.Add(2)	for i := 0; i < 2; i++ {		go func(id int) {			defer writeWg.Done()			fmt.Printf("写任务 %d 开始\n", id)			time.Sleep(time.Millisecond * 800) // 模拟耗时操作			fmt.Printf("写任务 %d 完成\n", id)		}(i)	}	// 分别等待两组任务完成	fmt.Println("等待所有读任务完成...")	readWg.Wait()	fmt.Println("所有读任务已完成!")	fmt.Println("等待所有写任务完成...")	writeWg.Wait()	fmt.Println("所有写任务已完成!")	fmt.Println("程序结束")}

运行这段代码,你会看到输出顺序大致如下(由于并发,具体顺序可能略有不同):

等待所有读任务完成...读任务 0 开始读任务 1 开始读任务 2 开始写任务 0 开始写任务 1 开始读任务 0 完成读任务 1 完成读任务 2 完成所有读任务已完成!等待所有写任务完成...写任务 0 完成写任务 1 完成所有写任务已完成!程序结束

高级技巧:嵌套或组合 WaitGroup

有时你可能希望在一个主 WaitGroup 中包含多个子 WaitGroup。虽然 Go 本身不直接支持嵌套 WaitGroup,但你可以通过在子 WaitGroup 的 Wait() 方法完成后调用主 WaitGroup 的 Done() 来实现类似效果。

// 示例:主 WaitGroup 等待子任务组完成var mainWg sync.WaitGroupmainWg.Add(1)go func() {	defer mainWg.Done()	var subWg sync.WaitGroup	subWg.Add(2)	// 启动子任务...	subWg.Wait() // 等待子任务完成}()mainWg.Wait() // 等待主任务(包含子任务)完成

常见误区与注意事项

  • 不要复制 WaitGroup:WaitGroup 包含一个不能被复制的 mutex,应始终以指针或引用方式传递。
  • Add() 调用时机:应在启动 goroutine 之前 调用 Add(),否则可能导致竞态条件。
  • 避免负计数:调用 Done() 的次数不能超过 Add() 增加的次数,否则会 panic。

总结

通过本文,你已经掌握了在 Go语言并发编程 中使用多个WaitGroup 的核心技巧。合理运用多个 WaitGroup 可以让你的并发程序结构更清晰、逻辑更可控。记住,Go并发同步机制 是构建高性能 Go 应用的基础,而 WaitGroup 正是其中最常用、最实用的工具之一。

希望这篇WaitGroup使用教程对你有所帮助!动手试试吧,实践是最好的学习方式。