Golang 条件变量

Jackey Golang 1,528 次浏览 , 没有评论

条件变量的作用并不保证同一时刻仅有一个协程(线程)访问某个共享的数据资源,而是在对应的共享数据的状态发生变化时,通知阻塞在某个条件上的协程(线程)。条件变量不是锁,在兵法中不能达到同步的目的,因此条件变量总是与锁一起使用。

Go标准库中的sync.Cond类型代表了条件变量。条件鼻梁要与锁(互斥锁或者读写锁)一起使用,成员变量L代表与条件变量代培使用的锁。

type Cond struct {
    noCopy noCopy

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

    notify  notifyList
    checker copyChecker
}

对应的有3个方法:Wait, Signal, Broadcast

  1. func (c *Cond) Wait()
    该函数的作用可归纳为如下三点:

    1. 阻塞等待条件变量满足
    2. 释放已掌握的互斥锁,相当于cond.L.Unlock(). 注意:两步为一个原子操作
    3. 当被唤醒,Wait()函数返回时,解除阻塞并重新获取互斥锁。相当于cond.L.Lock()
  2. func (c *Cond) Signal()
    单发通知,给一个正等待(阻塞)在该条件变量上的goroutine(线程) 发送通知
  3. func (c *Cond) Broadcast()
    广播通知,给正在等待(阻塞)在该条件变量上的所有goroutine(线程)发送通知

使用实例:

var cond sync.Cond

//生产者
func producer(out chan<- int, index int) {
    for {
        cond.L.Lock()
        for len(out) == 10 {
            fmt.Println(index, "len == 10")
            cond.Wait() //阻塞 等待
        }
        num := rand.Intn(800)
        time.Sleep(time.Second)
        out <- num
        fmt.Println("生产者:", index, num)

        cond.L.Unlock()
        cond.Signal() //唤醒阻塞的协程
    }
}

//消费者
func consumer(in <-chan int, index int) {
    for {
        cond.L.Lock()
        for len(in) == 0 {
            fmt.Println(index, "len == 0")
            cond.Wait() //阻塞 等待
        }
        time.Sleep(time.Second)
        num := <-in
        fmt.Println("消费者:", index, num)
        cond.L.Unlock()
        cond.Signal() //唤醒阻塞的协程
    }

}
func main() {
    ch := make(chan int, 10)
    rand.Seed(time.Now().UnixMilli())
    cond.L = new(sync.Mutex)

    for i := 1; i <= 4; i++ {
        go producer(ch, i)
    }

    for i := 1; i <= 6; i++ {
        go consumer(ch, i)
    }

    quit := make(chan []struct{})
    <-quit

}

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Go