Go WaitGroup及Cond底层实现原理是什么

发布时间:2022-08-23 17:48:40 作者:iii
来源:亿速云 阅读:169

Go WaitGroup及Cond底层实现原理是什么

在Go语言中,sync.WaitGroupsync.Cond是两个常用的同步原语,用于协调多个goroutine的执行。本文将深入探讨它们的底层实现原理,帮助读者更好地理解它们的工作机制。

1. sync.WaitGroup 的底层实现

sync.WaitGroup 是Go语言中用于等待一组goroutine完成执行的同步工具。它通过计数器来跟踪未完成的goroutine数量,并提供AddDoneWait三个方法来操作计数器。

1.1 WaitGroup 的结构

sync.WaitGroup 的结构定义如下:

type WaitGroup struct {
    noCopy noCopy

    state1 [3]uint32
}

WaitGroup 的核心是一个长度为3的uint32数组state1,它用于存储计数器和信号量。state1数组的前两个元素用于存储计数器,第三个元素用于存储信号量。

1.2 Add 方法

Add 方法用于增加或减少WaitGroup的计数器。其实现如下:

func (wg *WaitGroup) Add(delta int) {
    statep, semap := wg.state()
    state := atomic.AddUint64(statep, uint64(delta)<<32)
    v := int32(state >> 32)
    w := uint32(state)
    if v < 0 {
        panic("sync: negative WaitGroup counter")
    }
    if w != 0 && delta > 0 && v == int32(delta) {
        panic("sync: WaitGroup misuse: Add called concurrently with Wait")
    }
    if v > 0 || w == 0 {
        return
    }
    if *statep != state {
        panic("sync: WaitGroup misuse: Add called concurrently with Wait")
    }
    *statep = 0
    for ; w != 0; w-- {
        runtime_Semrelease(semap, false, 0)
    }
}

Add 方法首先通过state()函数获取state1数组的指针和信号量的指针。然后,它使用原子操作atomic.AddUint64来增加或减少计数器。如果计数器变为负数,Add方法会抛出panic。如果计数器变为0,Add方法会释放所有等待的goroutine。

1.3 Done 方法

Done 方法用于减少WaitGroup的计数器,其实现如下:

func (wg *WaitGroup) Done() {
    wg.Add(-1)
}

Done 方法实际上是调用了Add方法,并将参数设置为-1。

1.4 Wait 方法

Wait 方法用于阻塞当前goroutine,直到WaitGroup的计数器变为0。其实现如下:

func (wg *WaitGroup) Wait() {
    statep, semap := wg.state()
    for {
        state := atomic.LoadUint64(statep)
        v := int32(state >> 32)
        w := uint32(state)
        if v == 0 {
            return
        }
        if atomic.CompareAndSwapUint64(statep, state, state+1) {
            runtime_Semacquire(semap)
            if *statep != 0 {
                panic("sync: WaitGroup is reused before previous Wait has returned")
            }
            return
        }
    }
}

Wait 方法首先通过state()函数获取state1数组的指针和信号量的指针。然后,它在一个循环中不断检查计数器的值。如果计数器变为0,Wait方法会立即返回。否则,它会使用原子操作atomic.CompareAndSwapUint64来增加等待的goroutine数量,并调用runtime_Semacquire来阻塞当前goroutine。

1.5 总结

sync.WaitGroup 的底层实现依赖于原子操作和信号量。它通过计数器来跟踪未完成的goroutine数量,并使用信号量来阻塞和唤醒等待的goroutine。WaitGroup 的设计使得它能够高效地协调多个goroutine的执行。

2. sync.Cond 的底层实现

sync.Cond 是Go语言中用于条件变量的同步工具。它允许goroutine在某个条件不满足时进入等待状态,并在条件满足时被唤醒。

2.1 Cond 的结构

sync.Cond 的结构定义如下:

type Cond struct {
    noCopy noCopy

    L Locker

    notify  notifyList
    checker copyChecker
}

Cond 的核心是一个notifyList结构,它用于存储等待的goroutine。Cond 还包含一个Locker接口,用于在操作条件变量时加锁。

2.2 Wait 方法

Wait 方法用于使当前goroutine进入等待状态,直到条件变量被唤醒。其实现如下:

func (c *Cond) Wait() {
    c.checker.check()
    t := runtime_notifyListAdd(&c.notify)
    c.L.Unlock()
    runtime_notifyListWait(&c.notify, t)
    c.L.Lock()
}

Wait 方法首先调用checker.check()来检查Cond是否被复制。然后,它调用runtime_notifyListAdd将当前goroutine添加到等待列表中,并释放锁。接着,它调用runtime_notifyListWait使当前goroutine进入等待状态。当条件变量被唤醒时,Wait方法会重新获取锁并返回。

2.3 Signal 方法

Signal 方法用于唤醒一个等待的goroutine。其实现如下:

func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify)
}

Signal 方法首先调用checker.check()来检查Cond是否被复制。然后,它调用runtime_notifyListNotifyOne来唤醒一个等待的goroutine。

2.4 Broadcast 方法

Broadcast 方法用于唤醒所有等待的goroutine。其实现如下:

func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
}

Broadcast 方法首先调用checker.check()来检查Cond是否被复制。然后,它调用runtime_notifyListNotifyAll来唤醒所有等待的goroutine。

2.5 总结

sync.Cond 的底层实现依赖于notifyList结构和runtime包中的通知机制。它通过notifyList来管理等待的goroutine,并使用runtime包中的函数来实现goroutine的阻塞和唤醒。Cond 的设计使得它能够高效地协调多个goroutine的执行。

3. WaitGroupCond 的比较

sync.WaitGroupsync.Cond 都是Go语言中用于同步的工具,但它们的使用场景和实现方式有所不同。

4. 总结

sync.WaitGroupsync.Cond 是Go语言中两个重要的同步原语,它们的底层实现依赖于原子操作、信号量和runtime包中的通知机制。通过深入理解它们的实现原理,我们可以更好地使用它们来协调多个goroutine的执行,从而提高程序的并发性能。

希望本文能够帮助读者更好地理解sync.WaitGroupsync.Cond的底层实现原理,并在实际开发中灵活运用它们。

推荐阅读:
  1. ArrayList和LinkedList底层实现原理是什么
  2. HashMap的底层实现原理是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

go waitgroup cond

上一篇:vue底层是不是用node写的

下一篇:Java对象初始化过程代码块和构造器的调用顺序是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》