极客时间--golang并发编程实战课--Cond的学习总结

发布时间 2023-06-04 14:27:27作者: 99号的格调

Go标准库的Cond目的是:为等待/通知场景下的并发问题提供支持。Cond通常应用于等待某个条件的一组goroutine,等条件变为true的时候,其中一个goroutine或者所有的goroutine都会被唤醒。

Cond是和某个条件相关,这个条件需要一组goroutine协作共同完成,当条件还没有满足的时候,所有等待这个条件的goroutine都会被阻塞住,只有这一组goroutine通过协作达到了这个条件,等待的goroutine才可能继续进行下去。

等待条件:某个变量达到了某个阈值或者某个时间点,也可以是一组变量分别都达到了某个阈值,还可以是某个对象的状态满足了特定的条件。总结来讲,等待的条件是一种可以用来计算结果是 true 还是 false 的条件。

Cond的基本用法

标准库中的Cond初始化的时候,需要关联一个Locker接口的实例,通常使用Mutex和RMutex

结构:

type Cond struct {
    noCopy noCopy

    // L is held while observing or changing the condition
    L Locker

    notify  notifyList
    checker copyChecker
}

Cond关联的Locker实例可以通过c.L访问,它内部维护着一个先入先出的队列

Signal 方法允许调用者 Caller 唤醒一个等待此 Cond 的 goroutine。如果此时没有等待的 goroutine,显然无需通知 waiter;如果 Cond 等待队列中有一个或者多个等待的goroutine,则需要从等待队列中移除第一个 goroutine 并把它唤醒。

Broadcast 方法允许调用者 Caller 唤醒所有等待此 Cond 的 goroutine。如果此时没有等待的 goroutine,显然无需通知 waiter;如果 Cond 等待队列中有一个或者多个等待的goroutine,则清空所有等待的 goroutine,并全部唤醒。syil

Wait 方法,会把调用者 Caller 放入 Cond 的等待队列中并阻塞,直到被 Signal 或者Broadcast 的方法从等待队列中移除并唤醒。调用 Wait 方法时必须要持有 c.L 的锁。

来看一个例子:

package main

import (
    "log"
    "math/rand"
    "sync"
    "time"
)

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("所有运动员准备就绪")
}

附课程该章节的总结图: