简介
sync.Cond 是基于互斥锁/读写锁实现的条件变量,用来协调想要访问共享资源的那些 Goroutine。当共享资源状态发生变化时,sync.Cond 可以用来通知等待条件发生而阻塞的 Goroutine。
与互斥锁的关系
首先,条件变量是基于互斥锁/读写锁实现的,条件变量的初始化离不开互斥锁。
其次,互斥锁的目的是为了保护临界区和共享资源,而条件变量是为了协调想要访问这些共享资源的协程。
使用场景
我可以使用条件变量来实现生产者消费者场景等。
如下:
import (
"fmt"
"sync"
"time"
)
var stage = 0
var lock sync.Mutex
var pCond = sync.NewCond(&lock) //等待生产的条件变量
var cCond = sync.NewCond(&lock) //等待消费的条件变量
func produce() {
pCond.L.Lock() //先获取条件变量中的锁
defer pCond.L.Unlock()//使用defer函数在方法执行完之后解锁
for stage == 1 { //使用 for 判断当前条件是否需要等待,避免假唤醒,当有商品时,不生产。
pCond.Wait()//等待商品被消费后的通知
}
//正常生产商品
stage = 1
fmt.Println("生产成功")
//通知消费者消费
cCond.Signal()//唤醒最靠前的一个协程
}
func consume() {
cCond.L.Lock()
for stage == 0 {
cCond.Wait()
}
//正常消费商品
stage = 0
fmt.Println("消费成功")
cCond.L.Unlock()
//通知生产者生产
pCond.Signal()
}
func main() {
count := 5
for i := 0; i < count; i++ {
go consume()
go produce()
}
time.Sleep(time.Millisecond * 1000)
}
----------------------------------
生产成功
消费成功
生产成功
消费成功
生产成功
消费成功
生产成功
消费成功
生产成功
消费成功
Signal 和 Boardcast 的区别
首先,cond.Wait() 会将当前协程添加到通知队伍的队尾,当使用 Signal 唤醒等待的协程时,只能唤醒队伍首部的协程。
而使用 Boardcast 会唤醒所有等待的协程。
注意事项
- Wait方法需要在它基于的互斥锁保护下执行,否则就会引发不可恢复的 panic,所以我们在调用该方法之前需要先获取其基于的互斥锁。
- Signal 和 Boardcast 方法不需要受到互斥锁的保护,而且最好不要在解锁互斥锁之前调用这两个方法。