优化WaitGroup使用方式减少锁竞争
- 批量操作:避免在循环中单个地调用
wg.Add(1)
和wg.Done()
,而是批量地增加和减少计数。例如,假设要启动1000个goroutine,如果每次循环调用wg.Add(1)
,会产生1000次锁操作;可以在循环外先调用wg.Add(1000)
,在goroutine内部统一调用wg.Done()
。
var wg sync.WaitGroup
wg.Add(1000)
for i := 0; i < 1000; i++ {
go func() {
defer wg.Done()
// 业务逻辑
}()
}
wg.Wait()
- 复用WaitGroup:如果有重复的一组并发任务,可以复用同一个
WaitGroup
实例,而不是每次都创建新的。这减少了WaitGroup
初始化的开销。
合理设置Context超时时间
- 分析业务场景:对于实时性要求高的业务,如实时监控数据获取,超时时间应设置较短,比如几秒;而对于一些批处理任务,可能可以容忍较长时间,如几分钟。
- 动态调整:根据系统负载、网络状况等动态调整超时时间。例如,可以通过监控系统的资源利用率,当CPU或内存使用率高时,适当延长超时时间,防止任务因资源紧张而被误判超时。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 使用ctx执行任务
利用CancelFunc实现高效任务取消
- 及时取消:在主程序中,一旦满足某些条件(如用户手动取消、子任务出现错误等),立即调用
CancelFunc
。
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
return
default:
// 业务逻辑
}
}
}()
// 满足取消条件时
cancel()
- 链式传递:在调用多个函数,每个函数都启动子goroutine的场景下,将
Context
链式传递下去,确保整个任务链都能响应取消信号。
性能调优权衡因素
- 准确性与及时性:较短的超时时间可能会导致任务被误判超时,影响业务准确性;而较长的超时时间则可能导致资源长时间被占用,影响及时性和系统整体性能。
- 资源开销:复用
WaitGroup
减少锁竞争能提高性能,但如果复用不当,可能导致逻辑复杂,增加维护成本。动态调整超时时间需要额外的监控和计算资源。
- 代码复杂度:链式传递
Context
和复杂的取消逻辑虽然提高了任务管理的灵活性,但增加了代码的复杂度,可能导致调试和维护困难。