Golang:sync.WaitGroup和sync.Once

sync.WaitGroup

  • 开箱即用,并发安全。使用了原子操作。
  • 拥有三个指针方法:Add、Done、Wait。
  • WaitGroup的计数器值不能小于0,否则会引发panic。
  • 如果Wait方法的调用和首次Add方法的调用时同时发起的,比如,在两个同时启动的goroutine中,分别调用这两个方法,就有可能会引发panic。
  • 如果Wait方法执行期间,跨越了两个计数周期,就会引发panic,比如,当前goroutine调用Wait方法,阻塞了。另外一个goroutine调用了Done方法,计数器值变为0;此时会唤醒当前goroutine,并且试图继续执行Wait方法中其余代码。这时,又有一个goroutine调用了Add方法,此时Wait方法就会抛出panic sync: WaitGroup is reused before previous Wait has returned
  • 使用原则:不要把增加其计数器值的操作和调用其Wait方法的代码,放在不同的 goroutine 中执行。换句话说,要杜绝对同一个WaitGroup值的两种操作的并发执行。
  • 最好统一Add,并发Done,最后Wait
func coordinateWithWaitGroup() {
 total := 12
 stride := 3
 var num int32
 fmt.Printf("The number: %d [with sync.WaitGroup]\n", num)
 // 开始阶段不知道总共有多少,所以采取分批的方式
 var wg sync.WaitGroup
 for i := 1; i <= total; i = i + stride {
  // 启动stride个goroutine
  wg.Add(stride)
  for j := 0; j < stride; j++ {
   go addNum(&num, i+j, wg.Done)
  }
  // 等待其全部完成后开始下一批
  wg.Wait()
 }
 fmt.Println("End.")
}

sync.Once

  • 开箱即用,并发安全。使用了互斥锁和原子操作。
  • Do方法接收一个无参数无结果的函数,先检查是否做过(done是不是1,做过就返回),没做过加上锁,再检查是否做过(done是不是0,没做就做)。这两次条件判断被称为跨临界区的双重检查
type Once struct {
	m    Mutex
	done uint32
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 1 {
		return
	}
	// Slow-path.
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}
  • 如果参数函数执行很长时间或者根本不会结束,则可能导致相关goroutine阻塞。(因为不结束所以没解锁,所以其他的都抢不到锁)
  • 无论参数函数执行会以什么方式结束,done都会变为1,即便panic了,我们也没办法用同一个Once值重新执行了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值