Go sync.Cond用法

145 阅读2分钟

Cond 实现了一个条件变量,在 Locker 的基础上增加的一个消息通知的功能,保存了一个通知列表,用来唤醒一个或所有因等待条件变量而阻塞的 Go 程,以此来实现多个 Go 程间的同步。

type Cond struct {
    // L is held while observing or changing the condition
	L Locker
	// contains filtered or unexported fields
}

// 创建一个带锁的条件变量,Locker 通常是一个 *Mutex 或 *RWMutex
func NewCond(l Locker) *Cond

// 唤醒所有因等待条件变量 c 阻塞的 goroutine
func (c *Cond) Broadcast()

// 唤醒一个因等待条件变量 c 阻塞的 goroutine
func (c *Cond) Signal()

// 等待 c.L 解锁并挂起 goroutine,在稍后恢复执行后,Wait 返回前锁定 c.L,只有当被 Broadcast 和 Signal 唤醒,Wait 才能返回
func (c *Cond) Wait()

在调用Singnal, Broadcast之前应该确保goroutine进入Wait阻塞状态 Cond代码示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func main() {
    for i := 0; i < 10; i++ {
        go func(x int) {
            cond.L.Lock()         //获取锁
            defer cond.L.Unlock() //释放锁
            cond.Wait()           //等待通知,阻塞当前goroutine
            fmt.Println(x)
        }(i)
    }   
    time.Sleep(time.Second * 1)	// 睡眠1秒,使所有goroutine进入 Wait 阻塞状态
    fmt.Println("Signal...")
    cond.Signal()               // 1秒后下发一个通知给已经获取锁的goroutine
    time.Sleep(time.Second * 1)
    fmt.Println("Signal...")
    cond.Signal()               // 1秒后下发下一个通知给已经获取锁的goroutine
    time.Sleep(time.Second * 1)
    cond.Broadcast()            // 1秒后下发广播给所有等待的goroutine
    fmt.Println("Broadcast...")
    time.Sleep(time.Second * 1)	// 睡眠1秒,等待所有goroutine执行完毕
}

代码执行流程分析:

  1. 创建十个协程阻塞等待,其中某一个抢到了cond.L.Lock()这个锁,另外九个没抢到,抢到cond.L.Lock()锁的协程被cond.Wait()这一步阻塞,没抢到cond.L.Lock()锁的另外九个阻塞在cond.L.Lock()这一步

  2. main函数睡眠一秒之后调用cond.Signal()唤醒阻塞在cond.Wait()那一步的协程,之后这个协程执行完毕顺利退出,cond.L.Lock()锁被随机分配给了另外九个协程中的一个

  3. main函数再次睡眠一秒之后调用cond.Signal()唤醒阻塞在cond.Wait()那一步的协程,这个协程执行完毕之后顺利退出,cond.L.Lock()锁被随机分配给了另外八个协程中的一个

  4. main函数再次睡眠一秒之后调用cond.Broadcast()通知所有含有cond.Wait()的协程(Broadcast无视协程是否获得了cond.L.Lock()锁,会通知所有的cond.Wait()的协程)

  5. 此时八个协程不再被cond.Wait()阻塞,八个协程依次随机的获得cond.L.Lock()锁然后再释放cond.L.Lock()锁

参考:cloud.tencent.com/developer/a…