go语言学习笔记26------生产者消费者简易模型②

1.条件变量

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

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

type Cond struct {
noCopy noCopy
// L is held while observing or changing the condition
L Locker
checker copyChecke
}

对应的有3个常用方法,Wait,Signal,Broadcast。
1)func (c *Cond) Wait()
该函数的作用可归纳为如下三点:
a)阻塞等待条件变量满足
b)释放已掌握的互斥锁相当于cond.L.Unlock()。 注意:两步为一个原子操作。
c)当被唤醒,Wait()函数返回时,解除阻塞并重新获取互斥锁。相当于cond.L.Lock()

2)func (c *Cond) Signal()
单发通知,给一个正等待(阻塞)在该条件变量上的goroutine(线程)发送通知。

3)func (c *Cond) Broadcast()
广播通知,给正在等待(阻塞)在该条件变量上的所有goroutine(线程)发送通知。
下面我们用条件变量来编写一个优化“生产者消费者模型”
示例代码:

package main

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

//全局变量条件变量
var cond sync.Cond

func producer(ch chan<- int ,idx int) {
   //循环生产
   for{
      //条件变量互斥加锁
      cond.L.Lock()
      //当管道满后阻塞等待消费者处理
      for len(ch)==3{
         cond.Wait()
      }
      //随机生产数据并写入管道
      num:=rand.Intn(500)
      ch<-num
      fmt.Printf("%d生产者正在生产数据%d,缓冲区还剩数据位置%d\n",idx,num,len(ch))
      //条件变量互斥解锁
      cond.L.Unlock()
      //唤醒对端
      cond.Signal()

   }

}

func consumer(ch <-chan int ,idx int) {
   //循环消费
   for {
      //条件变量互斥加锁
      cond.L.Lock()
      //当管道没有内容时阻塞等待生产者处生产
      for len(ch)==0{
         cond.Wait()
      }
      //从管道中读取数据
      num:=<-ch
      fmt.Printf("%d消费者正在消费数据%d,缓冲区还剩数据位置%d\n",idx,num,len(ch))
      //条件变量互斥解锁
      cond.L.Unlock()
      //唤醒对端
      cond.Signal()
   }

}
func main() {
//创建随机数种子
   rand.Seed(time.Now().UnixNano())
//创建通信管道
   ch := make(chan int,3)
   quit:=make(chan bool)
//创建互斥锁和条件变量
   cond.L = new(sync.Mutex)
//3个生产者
   for i := 0; i < 3; i++ {
      go producer(ch,i+1)
   }
//3个消费者
   for i := 0; i < 3; i++ {
      go consumer(ch,i+1)
   }
//阻塞程序,不结束主进程
   <-quit
}
//输出结果
//1生产者正在生产数据217,缓冲区还剩数据位置1
//1生产者正在生产数据4,缓冲区还剩数据位置2
//1生产者正在生产数据311,缓冲区还剩数据位置3
//3消费者正在消费数据217,缓冲区还剩数据位置2
//3消费者正在消费数据4,缓冲区还剩数据位置1
//3消费者正在消费数据311,缓冲区还剩数据位置0
//1生产者正在生产数据444,缓冲区还剩数据位置1
//1生产者正在生产数据339,缓冲区还剩数据位置2
//2消费者正在消费数据444,缓冲区还剩数据位置1
//2消费者正在消费数据339,缓冲区还剩数据位置0
//2生产者正在生产数据371,缓冲区还剩数据位置1
//2生产者正在生产数据162,缓冲区还剩数据位置2
//2生产者正在生产数据386,缓冲区还剩数据位置3
//2消费者正在消费数据371,缓冲区还剩数据位置2
//2消费者正在消费数据162,缓冲区还剩数据位置1
//2消费者正在消费数据386,缓冲区还剩数据位置0

1)main函数中定义quit,其作用是让主协程阻塞。

2)定义product作为队列,生产者产生数据保存至队列中,最多存储3个数据,消费者从中取出数据模拟消费

3)条件变量要与锁一起使用,这里定义全局条件变量cond,它有一个属性:L Locker。是一个互斥锁。

4)开启3个消费者协程,开启3个生产者协程。

5)producer生产者,在该方法中开启互斥锁,保证数据完整性。并且判断队列是否满,如果已满,调用wait()让该goroutine阻塞。当消费者取出数后执行cond.Signal(),会唤醒该goroutine,继续生产数据。

6)consumer消费者,同样开启互斥锁,保证数据完整性。判断队列是否为空,如果为空,调用wait()使得当前goroutine阻塞。当生产者产生数据并添加到队列,执行cond.Signal() 唤醒该goroutine。

你可能感兴趣的:(Go语言与区块链)