题目
使用goroutine和channel实现并发数组求和
信息
- 类型:问答
- 难度:⭐
考点
goroutine基础使用,channel通信,WaitGroup同步
快速回答
使用Go并发计算数组元素之和的核心步骤:
- 将数组拆分为多个子段
- 为每个子段启动goroutine进行局部求和
- 使用channel传递局部结果
- 用sync.WaitGroup等待所有goroutine完成
- 主goroutine收集并累加最终结果
原理说明
Go并发模型基于CSP理论,通过goroutine实现轻量级线程,channel用于goroutine间通信。sync.WaitGroup提供简单的同步机制,确保主goroutine等待所有工作完成。
代码示例
package main
import (
"fmt"
"sync"
)
func sumWorker(nums []int, ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
sum := 0
for _, num := range nums {
sum += num
}
ch <- sum // 将局部和发送到channel
}
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
ch := make(chan int)
var wg sync.WaitGroup
// 将数组拆分为2个子段
wg.Add(2)
go sumWorker(nums[:len(nums)/2], ch, &wg)
go sumWorker(nums[len(nums)/2:], ch, &wg)
// 启动收集结果的goroutine
go func() {
wg.Wait()
close(ch) // 所有worker完成后关闭channel
}()
// 主goroutine收集结果
total := 0
for partialSum := range ch {
total += partialSum
}
fmt.Println("总和:", total) // 输出: 总和: 55
}最佳实践
- 任务拆分:根据CPU核心数动态确定goroutine数量
- 资源管理:使用defer wg.Done()确保计数器释放
- 通道安全:由独立goroutine关闭channel,避免向已关闭channel发送数据
- 错误处理:可增加错误通道(error channel)收集异常
常见错误
- 死锁:忘记wg.Add()或未启动收集结果的goroutine
- 竞态条件:多个goroutine同时写共享变量(本例使用channel避免)
- 通道阻塞:未及时接收channel数据导致goroutine阻塞
- 指针误用:传递WaitGroup时未使用指针导致副本问题
扩展知识
- 工作池模式:使用buffered channel创建固定数量的worker
- Context控制:添加超时和取消机制
- 原子操作:sync/atomic包实现无锁累加(但channel方案更符合Go哲学)
- 性能考量:任务拆分粒度影响性能,过小的任务会增加调度开销