生产者消费者模型及Golang简单实现

简介:介绍生产者消费者模型,及go简单实现的demo。

一、生产者消费者模型

生产者消费者模型:某个模块(函数等〉负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、协程、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
单单抽象出生产者和消费者,还够不上是生产者消费者模型。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。大概的结构如下图。

生产者消费者模型及Golang简单实现

假设你要寄一件快递,大致过程如下。.

1.把快递封好——相当于生产者制造数据。
2.把快递交给快递中心——相当于生产者把数据放入缓冲区。
3.邮递员把快递从快递中心取出——相当于消费者把数据取出缓冲区。

这么看,有了缓冲区就有了以下好处:
解耦:降低消费者和生产者之间的耦合度。有了快递中心,就不必直接把快递交给邮寄员,邮寄快递的人不对邮寄员产生任何依赖,如果某一个天邮寄员换人了,对于邮寄快递的人也没有影响。假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会真接影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合度也就相应降低了。
并发:生产者消费者数量不对等,依然能够保持正常通信。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者只能等着浪费时间。使用了生产者消费者模式之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。邮寄快递的人直接把快递扔个快递中心之后就不用管了。
缓存:生产者消费者速度不匹配,暂存数据。如果邮寄快递的人一次要邮寄多个快递,那么邮寄员无法邮寄,就可以把其他的快递暂存在快递中心。也就是生产者短时间内生产数据过快,消费者来不及消费,未处理的数据可以暂时存在缓冲区中。

二、Go语言实现

单向channel最典型的应用是”生产者消费者模型”。channel又分为有缓冲和无缓冲channel。channel中参数传递的时候,是作为引用传递。

1、无缓冲channel

示例代码一实现如下

package main

import "fmt"

func producer(out chan <- int) { for i:="0;" i<10; i++{ data :="i*i" fmt.println("生产者生产数据:", data) out <- 缓冲区写入数据 } close(out) 写完关闭管道 func consumer(in chan int){ 同样读取管道 for{ val, ok in if fmt.println("消费者拿到数据:", }else{ fmt.println("无数据") break 无需同步机制,先做后做 没有数据就阻塞等 fmt.println("消费者得到数据:", main(){ 传参的时候显式类型像隐式类型转换,双向管道向单向管道转换 ch 无缓冲channel go producer(ch) 子go程作为生产者 consumer(ch) 主go程作为消费者 < code></->

这里使用无缓冲channel,生产者生产一次数据放入channel,然后消费者从channel读取数据,如果没有只能等待,也就是阻塞,直到管道被关闭。所以宏观是生产者消费者同步执行。
另外:这里是只而外开辟一个go程执行生产者,主go程执行消费者,如果也是用一个新的go程执行消费者,就需要阻塞main函数中的go程,否则不等待消费者和生产者执行完毕,主go程退出,程序直接结束,如示例代码三。
生产者每一次生产,消费者也只能拿到一次数据,缓冲区作用不大。结果如下:

生产者消费者模型及Golang简单实现

2、有缓冲channel

示例代码二如下

package main

import "fmt"

func producer(out chan <- int) { for i:="0;" i<10; i++{ data :="i*i" fmt.println("生产者生产数据:", data) out <- 缓冲区写入数据 } close(out) 写完关闭管道 func consumer(in chan int){ 无需同步机制,先做后做 没有数据就阻塞等 in fmt.println("消费者得到数据:", main(){ 传参的时候显式类型像隐式类型转换,双向管道向单向管道转换 ch int, 5) 添加缓冲区,5 go producer(ch) 子go程作为生产者 consumer(ch) 主go程作为消费者 < code></->

有缓冲channel,只修改 ch := make(chan int, 5) // &#x6DFB;&#x52A0;&#x7F13;&#x51B2;一句,只要缓冲区不满,生产者可以持续向缓冲区channel放入数据,只要缓冲区不为空,消费者可以持续从channel读取数据。就有了异步,并发的特性。
结果如下:

生产者消费者模型及Golang简单实现
这里之所以终端生产者连续打印了大于缓冲区容量的数据,是因为终端打印属于系统调用也是有延迟的,IO操作的时候,生产者同时向管道写入,请求打印,管道的写入读取与终端输出打印速度不匹配。

三、实际应用

实际应用中,同时访问同一个公共区域,同时进行不同的操作。都可以划分为生产者消费者模型,比如订单系统。
很多用户的订单下达之后,放入缓冲区或者队列中,然后系统从缓冲区中去读来真正处理。系统不必开辟多个线程来对应处理多个订单,减少系统并发的负担。通过生产者消费者模式,将订单系统与仓库管理系统隔离开,且用户可以随时下单(生产数据)。如果订单系统直接调用仓库系统,那么用户单击下订单按钮后,要等到仓库系统的结果返回。这样速度会很慢。
也就是:用户变成了生产者,处理订单管理系统变成了消费者。

代码示例三如下

package main

import (
    "fmt"
    "time"
)

// &#x6A21;&#x62DF;&#x8BA2;&#x5355;&#x5BF9;&#x8C61;
type OrderInfo struct {
    id int
}

// &#x751F;&#x4EA7;&#x8BA2;&#x5355;--&#x751F;&#x4EA7;&#x8005;
func producerOrder(out chan <- orderinfo) { 业务生成订单 for i:="0;" i<10; i++{ order :="OrderInfo{id:" i+1} fmt.println("生成订单,订单id为:", order.id) out <- 写入channel } 如果不关闭,消费者就会一直阻塞,等待读 close(out) 订单生成完毕,关闭channel 处理订单--消费者 func consumerorder(in chan 从channel读取订单,并处理 in{ fmt.println("读取订单,订单id为:", main() ch orderinfo, 5) go producerorder(ch) consumerorder(ch) time.sleep(time.second * 2) < code></->

这里如上面逻辑类似,不同的是用一个,OrderInfo结构体模拟订单作为业务处理对象。主线程使用 time.Sleep(time.Second * 2)阻塞,否则,程序立即停止。
结果如下:

生产者消费者模型及Golang简单实现

Original: https://www.cnblogs.com/welan/p/15585536.html
Author: weilanhanf
Title: 生产者消费者模型及Golang简单实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516518/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Goland的那些实用技巧

    1、 自定义结构体tag 2、go mod tidy / download失败 解决办法:设置goproxy 3、取消/打开代码折叠 4、左侧project栏总是展示当前打开文件的…

    Go语言 2023年5月25日
    076
  • Ebiten-纯Golang开发的跨平台游戏引擎

    Go语言不是让你玩的啊喂! 昨天跟好基友聊开发的事,他说他等着闲下来的时候就用 PYGame写个像那个最近挺火的”文X游X”一样的游戏.(没收广告费啊!) …

    Go语言 2023年5月25日
    092
  • Go 语言实现 gRPC 的发布订阅模式,REST 接口和超时控制

    在多个平台的阅读量都创了新高,在 oschina 更是获得了首页推荐,阅读量到了 1w+,这已经是我单篇阅读的高峰了。 看来只要用心写还是有收获的。 这篇咱们还是从实战出发,主要介…

    Go语言 2023年5月25日
    070
  • 浅谈MatrixOne如何用Go语言设计与实现高性能哈希表

    目录 MatrixOne数据库是什么? 哈希表数据结构基础 哈希表基本设计与对性能的影响 碰撞处理 链地址法 开放寻址法 Max load factor Growth factor…

    Go语言 2023年5月25日
    059
  • 读 Go 源码,可以试试这个工具

    原文链接: 读 Go 源码,可以试试这个工具 编程发展至今,从面向过程到面向对象,再到现在的面向框架。写代码变成了一件越来越容易的事情。 学习基础语法,看看框架文档,几天时间搞出一…

    Go语言 2023年5月25日
    050
  • Go 中的 byte、rune 与 string

    byte 和 rune byte 是 uint8 的别名,其字面量是 8 位整数值,byte 切片相比于不可变的 string 方便常用许多。它可以更改每个字节或字符。这对于处理文…

    Go语言 2023年5月25日
    0130
  • 许式伟:Go+ 演进之路

    7 月 10 日,一年一度的 ECUG Con 2022 在线上圆满举行。许式伟作为七牛云 CEO、ECUG 社区发起人、Go+ 语言发明人,为大家来带了《Go+ 演进之路》的主题…

    Go语言 2023年5月25日
    090
  • 【golang详解】go语言GMP(GPM)原理和调度

    Goroutine调度是一个很复杂的机制,下面尝试用简单的语言描述一下Goroutine调度机制,想要对其有更深入的了解可以去研读一下源码。 首先介绍一下GMP什么意思: G &#…

    Go语言 2023年5月25日
    064
  • Go sort包

    sort包简介 官方文档Golang的sort包用来排序,二分查找等操作。本文主要介绍sort包里常用的函数,通过实例代码来快速学会使用sort包 sort包内置函数 sort.I…

    Go语言 2023年5月25日
    0105
  • Go之Logrus用法入门

    Logrus是Go (golang)的结构化日志程序,完全兼容标准库的API日志程序。Logrus is a structured logger for Go (golang), …

    Go语言 2023年5月25日
    069
  • 踩了个DNS解析的坑,但我还是没想通

    hello大家好,我是小楼。 最近踩了个DNS解析的小坑,虽然问题解决了,但排查过程比较曲折,最后还是有一点没有想通,整个过程分享给大家。 背景 最近负责的服务要置换机器。置换机器…

    Go语言 2023年5月25日
    0114
  • 【Docker】使用Docker Client和Docker Go SDK为容器分配GPU资源

    深度学习的环境配置通常是一项比较麻烦的工作,尤其是在多个用户共享的服务器上。虽然conda集成了virtualenv这样的工具用来隔离不同的依赖环境,但这种解决方案仍然没办法统一地…

    Go语言 2023年5月25日
    076
  • 微服务追踪SQL(支持Isto管控下的gorm查询追踪)

    效果图 SQL的追踪正确插入到微服务的调用链之间 详细记录了SQL的执行内容和消耗时间 搜索SQL的类型 多线程(goroutine)下的追踪效果 在 Kubernetes 中部署…

    Go语言 2023年5月25日
    070
  • EbitenCookBook中文教程 第一课:安装 Ebiten

    本文实时更新原址:https://ebitencookbook.vercel.app/docs/CookBook_Start/class1 第一课 安装 Ebiten 欢迎大家来到…

    Go语言 2023年5月25日
    064
  • 微服务-限流:一.golang实现令牌桶算法

    起初是因为要去拉取一些第三方的数据,而第三方的API接口都有限流措施。比如6000/分钟,500/分钟。想着拉取数据就用多个协程的方式。但是容易超频,所以想着写一个限流的东东。网上…

    Go语言 2023年5月25日
    050
  • 基于LSM的Key-Value数据库实现初篇

    前篇文章对LSM的基本原理,算法流程做了简单的介绍,这篇文章将实现一个简单的 基于LSM算法的 迷你Key-Value数据库,结合上篇文章的理论与本篇文章的实践使之对LSM算法有更…

    Go语言 2023年5月25日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球