一 条件变量
sync.Cond
类型即是Go中的条件变量,该类型内部包含一个锁接口。条件变量通常与锁配合使用:
创建条件变量的函数:
func NewCond(l locker) *Cond // 条件变量必须传入一个锁,二者需要配合使用
*sync.Cond
类型有三个方法:
- Wait: 该方法会阻塞等待条件变量满足条件。也会对锁进行解锁,一旦收到通知则唤醒,并立即锁定该锁
- Signal: 发送通知(单发),给一个正在等待在该条件变量上的协程发送通知
- Broadcast: 发送通知(广播),给正在等待该条件变量的所有协程发送通知,容易产生 惊群
示例:
func main() {
cond := sync.NewCond(&sync.Mutex{})
condition := false
// 开启一个新的协程,修改变量 condition
go func() {
time.Sleep(time.Second * 1)
cond.L.Lock()
condition = true // 状态变更,发送通知
cond.Signal() // 发信号
cond.L.Unlock()
}()
// main协程 是被通知的对象,等待通知
cond.L.Lock()
for !condition {
cond.Wait() // 内部释放了锁(释放后,子协程能拿到锁),并等待通知(消息)
fmt.Println("获取到了消息")
}
cond.L.Unlock() // 接到通知后,会被再次锁住,所以需要在需要的场合释放
fmt.Println("运行结束")
}
使用条件变量优化生产消费模型(支持多个生产者、多个消费者):
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// 定义缓冲区大小
const BUFLEN = 5
// 全局位置定义全局变量
var cond *sync.Cond = sync.NewCond(&sync.Mutex{})
// 生产者
func producer(ch chan<- int) {
for {
cond.L.Lock() // 给条件变量对应的互斥锁加锁
for len(ch) == BUFLEN { // 缓冲区满,则等待消费者消费,这里不能是if
cond.Wait()
}
ch <- rand.Intn(1000) // 写入缓冲区一个随机数
cond.L.Unlock() // 生产结束,解锁互斥锁
cond.Signal() // 一旦生产后,就唤醒其他被阻塞的消费者
time.Sleep(time.Second * 2)
}
}
// 消费者
func consumer(ch <-chan int) {
for {
cond.L.Lock() // 全局条件变量加锁
for len(ch) == 0 { // 如果缓冲区为空,则等待生产者生产,,这里不能是if
cond.Wait() // 挂起当前协程,等待条件变量满足,唤醒生产者
}
fmt.Println("Receive:", <-ch)
cond.L.Unlock()
cond.Signal()
time.Sleep(time.Second * 1)
}
}
func main() {
rand.Seed(time.Now().UnixNano()) // 设置随机数种子
// 生产消费模型中的
ch := make(chan int, BUFLEN)
// 启动10个生产者
for i := 0; i < 10; i++ {
go producer(ch)
}
// 启动10个消费者
for i := 0; i < 10; i++ {
go consumer(ch)
}
// 阻塞主程序退出
for {
}
}