使用WaitGroup可安全实现并发数据聚合,通过等待所有goroutine完成后再汇总结果,避免竞态条件,确保数据一致性。

在高并发场景下,Golang 凭借其轻量级的 goroutine 和高效的 channel 机制,非常适合用于数据聚合任务。实现并发数据聚合时,核心目标是安全、高效地将多个数据源的结果汇总,同时避免竞态条件和资源争用。以下是几种常见的实现方式与技巧。
使用 WaitGroup 控制协程生命周期
当需要等待多个 goroutine 完成后再进行结果汇总时,sync.WaitGroup 是最常用的同步工具。它确保主协程不会提前退出,所有子任务完成后再进行聚合。
示例:从多个模拟数据源获取数值并求和:
var wg sync.WaitGroup
results := make([]int, 4)
for i := 0; i < 4; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
// 模拟耗时操作
time.Sleep(100 * time.Millisecond)
results[i] = i * 2
}(i)
}
wg.Wait()
// 聚合结果
total := 0
for _, v := range results {
total += v
}
fmt.Println("Total:", total)
通过 Channel 汇聚数据
Channel 天然适合用于并发数据收集。每个 goroutine 将结果发送到共享 channel,主协程接收并聚合。这种方式解耦生产与消费,结构清晰。
立即学习“go语言免费学习笔记(深入)”;
常见做法:创建一个缓冲或非缓冲 channel,配合 goroutine 发送数据,使用 for-range 接收:
resultCh := make(chan int, 4)
for i := 0; i < 4; i++ {
go func(i int) {
resultCh <- i * i
}(i)
}
total := 0
for i := 0; i < 4; i++ {
total += <-resultCh
}
fmt.Println("Sum of squares:", total)
若不确定协程数量,可在关闭 channel 后使用 for-range 遍历:
close(resultCh)
for val := range resultCh {
total += val
}
结合 Mutex 保护共享状态
当多个 goroutine 需要更新同一个变量(如 map 或 slice)时,必须使用 sync.Mutex 防止数据竞争。
例如,聚合多个用户访问记录:
var mu sync.Mutex userCounts := make(map[string]int)for i := 0; i < 10; i++ { go func(user string) { mu.Lock() userCounts[user]++ mu.Unlock() }("user" + fmt.Sprintf("%d", i%3)) }
// 等待所有完成(此处简化为 sleep,实际应使用 WaitGroup) time.Sleep(time.Second) fmt.Println("User access counts:", userCounts)
使用 Atomic 操作优化简单类型
对于计数类聚合(如 int64),sync/atomic 提供无锁原子操作,性能优于 Mutex。
示例:统计请求数量:
var counter int64for i := 0; i < 100; i++ { go func() { atomic.AddInt64(&counter, 1) }() }
time.Sleep(time.Second) fmt.Println("Total requests:", atomic.LoadInt64(&counter))
基本上就这些。选择哪种方式取决于聚合的数据结构和并发模型。合理组合 WaitGroup、channel、Mutex 和 atomic,可以让 Golang 并发数据聚合既安全又高效。不复杂但容易忽略的是关闭 channel 的时机和避免死锁。










