您的位置:首页 > 编程语言 > Go语言

go使用条件变量实现生产者消费者模型

2018-09-07 20:41 1201 查看

概述

前面已经学习过使用channel完成简单的生产者消费者模型了,但那种模型存在一定的弊端,生产者消费者数量增加时,数据容易紊乱或被堵塞,而使用条件变量实现的生产者消费者模型可以有效解决这一点。

所需知识点

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

上代码之前我们首先需要知道其中重要的一个难点,即条件变量对应的三个常用方法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(线程)发送通知。
//一般情况下不使用broadcast,会引起惊群!所以我们下面使用Signal函数
理解以上知识点后,再来理解条件变量的使用就比较容易了,上代码:

代码

package main

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

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

func producter(ch chan<- int)  {
for  {
cond.L.Lock()    //go进程进来后,不多比比,先上锁
for len(ch)==3 { /*注意这里是for而不是if,判断缓冲
区里是不是有太多数据,如果满了,则持续阻塞,直至对面发来signal信号*/
cond.Wait()
}
num:=rand.Intn(1000)
ch<-num
fmt.Println("已生产",num)
cond.L.Unlock()//生产完毕后解锁并给对面发信号
cond.Signal()
time.Sleep(time.Millisecond*300)
}
}
func consumer(ch <-chan int)  {
for  {
cond.L.Lock()//以下都是跟上面一样的
for len(ch)==0 {
cond.Wait()
}
num:=<-ch
fmt.Println("已消费:",num)
cond.L.Unlock()
cond.Signal()
time.Sleep(time.Millisecond*300)
}
}
func main() {
//给全局变量加上一个锁的功能,相当于给他加个装备
cond.L=new(sync.Mutex)
var ch chan int=make(chan int,3)
var quit chan string
rand.Seed(time.Now().UnixNano())
for i:=0;i<5 ;i++  {
go producter(ch)
}
for i:=0;i<3 ;i++  {
go consumer(ch)
}
<-quit//这个管道是故意阻塞在这里,防止主进程结束
}

有问题的小伙伴可以给我留言哦

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: