实现方案
package main
import (
"fmt"
"sync"
)
// 模拟从不同数据源获取数据的函数
func getData1(wg *sync.WaitGroup, result *[]int) {
defer wg.Done()
// 模拟获取数据的操作
data := []int{1, 2, 3}
*result = append(*result, data...)
}
func getData2(wg *sync.WaitGroup, result *[]int) {
defer wg.Done()
// 模拟获取数据的操作
data := []int{4, 5, 6}
*result = append(*result, data...)
}
func aggregateData() []int {
var result []int
var wg sync.WaitGroup
wg.Add(2)
go getData1(&wg, &result)
go getData2(&wg, &result)
wg.Wait()
// 汇总和分析数据,这里简单示例,实际可进行复杂分析
sum := 0
for _, num := range result {
sum += num
}
fmt.Printf("汇总结果: %d\n", sum)
return result
}
避免死锁问题
- 确保 Add 和 Done 配对:在启动 goroutine 之前调用
wg.Add(n)
,其中 n
是需要等待的 goroutine 数量。在每个 goroutine 完成工作时调用 wg.Done()
。确保 wg.Done()
调用在 goroutine 结束之前,通常使用 defer wg.Done()
来保证即使 goroutine 内部发生错误也能正确标记完成。
- 避免重复调用 Add:不要在 goroutine 内部再次调用
wg.Add
,除非你清楚知道自己在做什么。如果需要动态增加等待的 goroutine 数量,要确保同步操作,避免竞争条件。
- 确保 Wait 在合适位置:
wg.Wait()
应该在所有启动的 goroutine 开始工作之后调用,并且在需要等待所有 goroutine 完成的逻辑位置调用,否则可能会提前返回导致数据未完全聚合。
WaitGroup 的优势
- 简单易用:Go 标准库提供的
sync.WaitGroup
结构简单,易于理解和使用,能够快速实现并发任务的等待同步。
- 性能高效:在底层实现上,
WaitGroup
使用信号量机制,能够高效地进行同步操作,不会引入过多的性能开销,适合高并发场景。
- 适合简单场景:对于只需要等待一组 goroutine 完成的简单并发场景,
WaitGroup
提供了足够的功能,不需要引入复杂的同步机制。
WaitGroup 的局限性
- 功能单一:
WaitGroup
仅能用于等待一组 goroutine 完成,对于更复杂的同步需求,如条件变量、读写锁等场景,WaitGroup
无法满足。
- 缺乏灵活性:
WaitGroup
一旦通过 Add
设置了等待的 goroutine 数量,就不能动态地修改这个数量(除了使用一些非标准的方式),在需要动态调整并发任务数量的场景下不够灵活。
- 错误处理不便:
WaitGroup
本身没有提供处理 goroutine 内部错误的机制,如果某个 goroutine 发生错误,WaitGroup
无法感知并做出相应处理,需要额外的错误处理逻辑。