问题
在写巡检脚本的时候,遇到了如下问题,特此记录。
最开始写的代码如下:
{
var wg sync.WaitGroup
for _, a := range activeList {
wg.Add(1)
go func(ia *model.ImportantActivity) {
defer wg.Done()
if err := GetBusinessData(ia); err != nil {
log.Error(err)
}
}(a)
wg.Wait()
}
}
整个执行下来需要100+ s ,跟没使用协程用了差不多的时间,一点效果没有。
观察自己写的协程好像也没有问题呀,但是什么好像并没有生效呢。
解决
偶然间看到了这段话,突然发现,我这里好像写的是假协程。因为如果把 wg.Add(1) 写在 for 循环里面的话,每次都要等一个for循环结束了,才会创建下一个协程,所以跟没使用协程效果一样。
golang是值拷贝传递。for循环很快,协程创建需要的时间大于for循环的时间。因为协程创建 需要进行 堆栈分配,上下文准备,以及与内核态的线程进行映射工作等。
golang for 循环创建协程问题 https://blog.csdn.net/gu864852213/article/details/119256885
对协程进一步理解后,修改如下:
{
var wg sync.WaitGroup
wg.Add(len(activeList))
for _, a := range activeList {
go func(ia *model.ImportantActivity) {
defer wg.Done()
if err := GetBusinessData(ia); err != nil {
log.Error(err)
}
}(a)
}
wg.Wait()
}
整个只执行了 15s。
总结
-
- wg.Add 和 wg.Wait 需要写在 for 循环外面,表示开启多少个协程,并等待协程都执行结束
- 使用 闭包 进行值传递,这样不会产生变量混乱
Go语言协程使用最佳实践 https://zhuanlan.zhihu.com/p/374464199
记一次golang经典错误–for循环中的go协程调用 https://baorongquan.github.io/2018/05/06/%E8%AE%B0%E4%B8%80%E6%AC%A1golang%E7%BB%8F%E5%85%B8%E9%94%99%E8%AF%AF-for%E5%BE%AA%E7%8E%AF%E4%B8%AD%E7%9A%84go%E5%8D%8F%E7%A8%8B%E8%B0%83%E7%94%A8/