看过啥底层包?

本文阅读 2 分钟
首页 golang 正文

题目来源:

频次:1

答案:engine

这里就简单说一下sync.WaitGroup

WaitGroup包含三个方法:

wg.Add(int)
wg.Done()
wg.Wait()

Add可以设置WaitGroup的计数值,一般放在前面写
Done用来将计数值-1,写在前置goroutine里
Wait会阻塞当前的goroutine,直到WaitGroup的计数值为0,写在需要等待的goroutine里,很多情况下是main goroutine。

使用WaitGroup后,可以确保主协程会在前面的goroutine结束之后才会继续。

nocopy字段

对于一些不应该被复制的结构体,可以在里面增加nocopy字段,它的底层是:

type noCopy struct{}

// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) UnLock() {}

通过go vet可以检测是否有不应该的复制:

Copy passes lock by value: main.Demo contains main.noCopy
源码解析
type WaitGroup struct {
  // noCopy字段,防止WaitGroup实例被copy
  noCopy noCopy

  // 前两个元素作为state,后一个元素作为信号量
  // 第一个是当前阻塞的goroutine个数,第二个是WaitGroup计数值,第三个是信号量
  // 对于32bit和64bit系统,字段有些许差别,后面有代码表示,这里不作讨论
  state1 [3]uint32
}
func (wg *WaitGroup) Add(delta int) {
  // 获取state和信号量
  statep, semap := wg.state()
  // 高32bit是计数值,所以把delta左移32位加到WaitGroup计数值上
  state := atomic.AddUint64(statep, uint64(delta)<<32)
  // 当前计数值
  v := int32(state >> 32)
  // 等待者数量
  w := uint32(state)
  // 计数值大于0或者w等于0,直接正常的返回
  if v > 0 || w == 0 {
    return
  }
  // 接下来的情况就是计数值等于0,但是还有等待者的情况,此时等待的已经没有意义
  // 比如说一开始只设了wg.Add(3),结果启动了五个goroutine里面都有Done,Done了三次之后,剩下的两个协程不会等它们执行完了。
  // 直接把组合的statep设为0(v和w都设为0)
  *statep = 0
  for ; w != 0; w-- {
    runtime_Semrelease(semap, false, 0)
  }
}
func (wg *WaitGroup) Done() {  // 非常简单,就是调Add(-1)即可  wg.Add(-1)}
// 一直阻塞,直到state的计数值=0func (wg *WaitGroup) Wait() {  statep, semap := wg.state()  for {    state := atomic.LoadUint64(statep)    v := int32(state >> 32)    w := uint32(state)    // 如果为0.直接返回    if v == 0 {      return    }    // 否则阻塞    if atomic.CompareAndSwapUint64(statep, state, state+1) {      runtime_Semacquire(semap)      return    }  }}
本文来自投稿,不代表本站立场,如若转载,请注明出处:
了解中间件吗?有什么好处?
« 上一篇 09-17
Go 高并发的特点
下一篇 » 09-17

发表评论

发表评论