一、前言
waitgroup在golang中,用于线程同步,指等待一个组,等待一个系列执行完成后,才会向下执行,可以解决一个 进程goroutine 等待多个该进程启动的子线程goroutine 都正常运行完成的场景,这个比较常见的场景就是例如 后端 main processer 启动了多个消费者worker干活,还有爬虫并发爬取数据,多线程下载等等,为了保证主进程在所有的子线程完成后再退出,这时就要用上waitgroup
二、waitgroup使用示例
我们这里模拟一个 worker 的例子
package main
import (
"fmt"
"sync"
"time"
)
func worker(idx int, out chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(1 * time.Second)
fmt.Println(time.Now())
fmt.Println(idx )
<-out
}
func main() {
wg := new(sync.WaitGroup)
in := make(chan struct{}, 20)
for i := 0; i < 200; i++ {
in <- struct{}{}
wg.Add(1)
go worker(i, in, wg)
}
wg.Wait()
}
在这段代码中,main最后一行是
wg.Wait()
这行代码保证有所的200个子线程全部都执行完成后才会退出main函数,如果没有最后一行wg.Wait(),可能会出现for循环遍历完程序就直接退出了,有可能只有不确定个几个子线程执行完成,其它线程由于主程序main退出后就直接退出了
从这个例子我们也可以看到 waitgroup通常配合来限制并发线程个数和确保所有的线程都最终都执行完成
这段代码中 ws 有三个缓冲,所以并发的数量是20,超过20个就要等待执行完成释放所占用通道后才能再开新的线程
上面的代码,会在执行到wg.Wait()后等待,直到所有的200线程全部执行完后才会继续往下执行
可以説这段代码非常精妙,示例代码执行结果如下:
同时我们也可以测试一下把最后一行
//wg.Wait()
注释掉,我们看一下程序会怎么执行
注释后,我们看到程序完成时,只有171个线程完成运行,剩下的20几个线程异常结束了,看不到任何返回结果
三、waitgroup使用注意事项
同时我们在使用waitgroup时也要注意一些坑:
1、 Add一个负数
如果计数器的值小于0会直接panic
2、 Add在Wait之后调用
比如一些子协程开头调用Add结束调用Wait,这些 Wait无法阻塞子协程。正确做法是在开启子协程之前先Add特定的值。
3、 未置为0就重用
WaitGroup可以完成一次编排任务,计数值降为0后可以继续被其他任务所用,但是不要在还没使用完的时候就用于其他任务,这样由于带着计数值,很可能出问题。
4、 复制waitgroup
WaitGroup有nocopy字段,不能被复制。也意味着WaitGroup不能作为函数的参数
四、waitgroup使用总结
WaitGroup是Golang应用开发过程中经常使用的并发控制技术,学习golang是我们必须要掌握和理解的机制之一,建议有时间了大家再进一步的研究一下WaitGroup的底层实现逻辑。
附:陷阱避免
1)WaitGroup 同步的是 goroutine, 如果在 goroutine 中进行 Add(1) 操作,可能在这些 goroutine 还没来得及 Add(1) 已经执行 Wait 操作,造成程序退出。
2)WaitGroup 传递给goroutine的时候,应该采用引用方式,从而避免发生副本拷贝而死锁。
总结
到此这篇关于golang基础之waitgroup用法以及使用要点的文章就介绍到这了,更多相关golang waitgroup用法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!