在sync包的同步原语中,sync.Cond可能是最少被使用和理解的。但是,它提供了我们无法通过channel实现的功能。下面我们通过一个具体的示例来展示sync.Cond何时有用以及如何使用它。
下面的示例实现了捐赠目标机制:每当达到特定目标时都会发出警报的应用程序。我们将有一个goroutine负责增加余额(一个更新器 goroutine )。其他 goroutine 将接收更新并在达到特定目标时打印一条消息(一些监听器 goroutine)。例如,一个 goroutine 正在等待10美元的捐赠目标,而另一个 goroutine 正在等待15美元的捐赠目标。
首先尝试的实现是使用互斥锁。更新器 goroutine 每秒增加余额,而那些监听器 goroutine 开始循环直到达到它们的捐赠目标:
type Donation struct {
mu sync.RWMutex
balance int
}
donation := &Donation{}
/ Listener goroutines
f := func(goal int) {
donation.mu.RLock()
for donation.balance < goal {
donation.mu.RUnlock()
donation.mu.RLock()
}
fmt.Printf("$%d goal reached\n", donation.balance)
donation.mu.RUnlock()
}
go f(10)
go f(15)
/ Updater goroutine
go func() {
for {
time.Sleep(time.Second)
donation.mu.Lock()
donation.balance++
donation.mu.Unlock()
}
}()
我们使用互斥锁保护对共享变量donation.balance的访问。如果我们运行这个例子,它会按预期工作:
$10 goal reached
$15 goal reached
主要问题--以及使这个实现变得糟糕的原因--是繁忙的循环。每个监听器 goroutine 一直循环,直到达到其捐赠目标,这会浪费大量CPU 周期并使CPU 使用量巨大。我们需要找到更好的解决方案。