go-cond实现

常见的等待通知机制:请实现一个限定容量的队列(queue),当队列满或者空的时候,利用等待 / 通知机制实现阻塞或者唤醒。。

在 Go 中,也可以实现一个类似的限定容量的队列,而且实现起来也比较简单,只要用条件变量(Cond)并发原语就可以。Cond 并发原语相对来说不是那么常用,但是在特定的场景使用会事半功倍,比如你需要在唤醒一个或者所有的等待者做一些检查操作的时候。

Go 标准库的 Cond

type Cond
func NeWCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()

Cond初始化时,需要关联一个实现Locker 接口的实例,一般用Mutex 或者 RWMutex。
Cond 关联的 Locker 实例可以通过 c.L 访问,它内部维护着一个先入先出的等待队列。

Signal 方法: 唤醒等待队列的一个go routine, 不要求一定要持有 c.L 的锁
Broadcast方法:唤醒等待队列的全部go routine, 不要求一定要持有 c.L 的锁
Wait方法:放入 Cond 的等待队列中并阻塞,必须持有c.L 的锁

func main() {
c := sync.NewCond(&sync.Mutex{})
var ready int

for i := 0; i < 10; i++ {
    go func(i int) {
        time.Sleep(time.Duration(rand.Int63n(10)) * time.Second)

        // 加锁更改等待条件
        c.L.Lock()
        ready++
        c.L.Unlock()

        log.Printf("运动员#%d 已准备就绪\n", i)
        // 广播唤醒所有的等待者
        c.Broadcast()
    }(i)
}

c.L.Lock()
for ready != 10 {
    c.Wait()
    log.Println("裁判员被唤醒一次")
}
c.L.Unlock()

//所有的运动员是否就绪
log.Println("所有运动员都准备就绪。比赛开始,3,2,1, ......")

}

type Cond struct {
noCopy noCopy

// 当观察或者修改等待条件的时候需要加锁
L Locker

// 等待队列
notify  notifyList
checker copyChecker

}

func NewCond(l Locker) *Cond {
return &Cond{L: l}
}

func (c *Cond) Wait() {
c.checker.check()
// 增加到等待队列中
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock()
// 阻塞休眠直到被唤醒
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
}

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

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

感觉和c中的pthread_cond_wait、 pthread_cond_notify相似,
对变量的更改需要加锁,然后cond是等待在此变量上的,要求wait时,
肯定先持有锁,然后加入等待队列,然后释放锁,进入休眠态。
变量更改和唤醒操作是两步,因此可能存在变量还未更改,就做了唤醒,称为“虚假唤醒”,
因此唤醒时要重新检查变量的值,不符合条件时,重新休眠

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值