目录

一 条件变量

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 {

    }
}