sync.WaitGroup和sync.Once
Posted linyihai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sync.WaitGroup和sync.Once相关的知识,希望对你有一定的参考价值。
sync.WaitGroup,顾名思义,等待一组goroutinue运行完毕。sync.WaitGroup声明后即可使用,它有如下方法:
- func (wg *WaitGroup) Add(delta int) #不能传入符数,否则引发panic
- func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
一般套路是,"先统一Add, 在并发Done, 最后Wait"。中心思想是由一个goroutine或主程序来调用Add和Wait方法,其他goroutinue作为子任务调用done方法,否则会引发可能的panic异常。
sync.Once, 也是开箱即用的,声明后即可使用,只有一个方法:
- func (o *Once) Do(f func())
sync.Once需要注意以下两点,避免踩坑:
- 当多个goroutinue并发地调用同一个sync.Once实例时,如果传入的函数执行时间过长,由于sync.Once是使用锁机制,保证传入的函数只执行一次,因此会造成其他goroutinue函数的阻塞。
- sync.Once 有一个done的unint32的字段,取值只有0和1。只要Do方法执行完成,done就由0变为1。值得注意的是done由defer保证执行后一定会是1。
package main
import "sync"
import "fmt"
import "sync/atomic"
import "time"
const PI = 3.14
var w sync.WaitGroup
var once sync.Once
var doneTaskCount int32
func calCircleCircum(r float64, ret *float64) {
defer w.Done()
*ret = PI * r
atomic.AddInt32(&doneTaskCount, 1)
time.Sleep(time.Millisecond * 2000)
}
func calRectangleCircum(width float64, long float64, ret *float64) {
defer w.Done()
*ret = 2 * (long + width)
atomic.AddInt32(&doneTaskCount, 1)
time.Sleep(time.Millisecond * 1000)
}
func printResults(circleRet *float64, rectangleRet *float64) {
once.Do(func(){
for atomic.LoadInt32(&doneTaskCount) !=2 {
fmt.Println("Wait sub task done.....")
time.Sleep(time.Millisecond * 200)
}
})
fmt.Printf("the cicle circumference is %1.2f
", *circleRet)
fmt.Printf("the rectangle circumference is %1.2f
", *rectangleRet)
}
func main() {
circleRet := 0.0
rectangleRet := 0.0
w.Add(2)
go printResults(&circleRet, &rectangleRet)
go calCircleCircum(2.0, &circleRet)
go calRectangleCircum(2.0, 3.0, &rectangleRet)
w.Wait()
}
以上是关于sync.WaitGroup和sync.Once的主要内容,如果未能解决你的问题,请参考以下文章
Golang的sync.WaitGroup 实现逻辑和源码解析
go语言学习笔记 — 进阶 — 并发编程(11):同步sync,等待组(sync.WaitGroup)—— 保证在并发环境中完成指定数量的任务