Golang Cond同步机制

条件变量是构建在一个基础锁上的同步原语,Golang Crondition位于sync包中,用于goroutine需要关注特定的条件的场景。
Cond 模块定义:

type Cond struct {
    noCopy noCopy

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

    notify  notifyList
    checker copyChecker
}
func NewCond(l Locker) *Cond

Cond需要指定一个Locker,通常是一个*Mutex或*RWMutex。

另外,Cond还定义了一下几个核心方法:

func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()

Broadcase、Signal

唤醒因wait condition而挂起goroutine,区别是Signal只唤醒一个,而Broadcast唤醒所有。允许调用者获取基础锁Locker之后再调用唤醒,但非必需。

Wait

必须获取该锁之后才能调用Wait()方法,Wait方法在调用时会释放底层锁Locker,并且将当前goroutine挂起,直到另一个goroutine执行Signal或者Broadcase,该goroutine才有机会重新唤醒,并尝试获取Locker,完成后续逻辑。

举例

生产者消费者问题是条件原语的一个典型例子。

package main

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

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

var capacity = 10
var consumerNum = 3
var producerNum = 5

func producer(out chan<- int) {
    for i := 0; i < producerNum; i++ {
        go func(nu int) {
            for {
                cond.L.Lock()
                for len(out) == capacity {
                    fmt.Println("Capacity Full, stop Produce")
                    cond.Wait()
                }
                num := rand.Intn(100)
                out <- num
                fmt.Printf("Produce %d produce: num %d\n", nu, num)
                cond.L.Unlock()
                cond.Signal()

                time.Sleep(time.Second)
            }
        }(i)
    }
}

func consumer(in <-chan int) {
    for i := 0; i < consumerNum; i++ {
        go func(nu int) {

            for {
                cond.L.Lock()
                for len(in) == 0 {
                    fmt.Println("Capacity Empty, stop Consume")
                    cond.Wait()
                }
                num := <-in
                fmt.Printf("Goroutine %d: consume num %d\n", nu, num)
                cond.L.Unlock()
                time.Sleep(time.Millisecond * 500)
                cond.Signal()
            }
        }(i)
    }
}

func main() {

    rand.Seed(time.Now().UnixNano())

    quit := make(chan bool)
    product := make(chan int, capacity)

    producer(product)
    consumer(product)

    <-quit
}