问题

在写巡检脚本的时候,遇到了如下问题,特此记录。

最开始写的代码如下:

{
    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/