sync.WaitGroup和sync.Once

sync.WaitGroup,顾名思义,等待一组goroutinue运行完毕。sync.WaitGroup声明后即可使用,它有如下方法:

  1. func (wg *WaitGroup) Add(delta int) #不能传入符数,否则引发panic
  2. func (wg *WaitGroup) Done()
  3. func (wg *WaitGroup) Wait()

    一般套路是,"先统一Add, 在并发Done, 最后Wait"。中心思想是由一个goroutine或主程序来调用Add和Wait方法,其他goroutinue作为子任务调用done方法,否则会引发可能的panic异常。

sync.Once, 也是开箱即用的,声明后即可使用,只有一个方法:

  1. func (o *Once) Do(f func())

sync.Once需要注意以下两点,避免踩坑:

  1. 当多个goroutinue并发地调用同一个sync.Once实例时,如果传入的函数执行时间过长,由于sync.Once是使用锁机制,保证传入的函数只执行一次,因此会造成其他goroutinue函数的阻塞。
  2. 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\n", *circleRet)
  fmt.Printf("the rectangle circumference is %1.2f\n", *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()
}

猜你喜欢

转载自www.cnblogs.com/linyihai/p/10285437.html