zoukankan      html  css  js  c++  java
  • Golang 入门系列(十七)几个常见的并发模型——生产者消费者模型

    前面已经讲过很多Golang系列知识,包括并发,锁等内容,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html

    接下来讲几个golang常见的并发模型,今天先说说生产者消费者模型。

     

    生产者消费者模型

     

    生产者:发送数据端

    消费者:接收数据端

    缓冲区:
      1. 解耦(降低生产者和消费者之间耦合度)

      2. 并发(生产者消费者数量不对等时,能保持正常通信)

      3. 缓存(生产者和消费者 数据处理速度不一致时,暂存数据)

    如何实现?

    生产者消费者模型是非常常见的并发模型,而且golang提供了chan类型,可以很方便的实现。

    根据 golang的官方文档,使用chan就可以实现生产者和消费者之间的数据和状态同步。

    • 通过chan在生产者和消费者之间传递数据(ch)和同步状态(done);
    • chan作为参数传递时是引用传递,不需要使用指针;
    • chan是协程安全的,多个goroutine之间不需要锁;
    • chan的close事件可以被recv获取,close事件一定在正常数据读完之后,机制类似于read到EOF;
    示例代码:

    package main

    import "fmt"

    func Producer(ch chan int) {
      for i := 1; i <= 10; i++ {
        ch <- i
      }
      close(ch)
    }

    func Consumer(id int, ch chan int, done chan bool) {
      for {
        value, ok := <-ch
        if ok {
          fmt.Printf("id: %d, recv: %d ", id, value)
        } else {
          fmt.Printf("id: %d, closed ", id)
          break
        }
      }
      done <- true
    }

    func main() {
      ch := make(chan int, 3)

      coNum := 2
      done := make(chan bool, coNum)
      for i := 1; i <= coNum; i++ {
        go Consumer(i, ch, done)
      }

      go Producer(ch)

      for i := 1; i <= coNum; i++ {
        <-done
      }
    }


    运行结果:
    id: 2, recv: 1
    id: 1, recv: 2
    id: 1, recv: 4
    id: 1, recv: 5
    id: 1, recv: 6
    id: 2, recv: 3
    id: 2, recv: 8
    id: 2, recv: 9
    id: 2, recv: 10
    id: 2, closed
    id: 1, recv: 7
    id: 1, closed

     

    简单说明:

    1、首先创建一个双向的channel,

    2.、然后开启一个新的goroutine,把双向通道作为参数传递到producer方法中,同时转成只写通道。子协程开始执行循环,向只写通道中添加数据,这就是生产者。

    3、主协程,直接调用consumer方法,该方法将双向通道转成只读通道,通过循环每次从通道中读取数据,这就是消费者。


    注意:channel作为参数传递,是引用传递。

    缓冲区的好处

    1:解耦
    假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会直接影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合度也就相应降低了。

    2:处理并发
    生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者只能无端浪费时间。
    使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。
    其实最当初这个生产者消费者模式,主要就是用来处理并发问题的。

    3:缓存
    如果生产者制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

     

    最后

    以上,就把golang生产者消费者模型简单介绍完了,希望能对大家有所帮助。



  • 相关阅读:
    Emqx启用Redis认证
    写在2021末
    如何让 Spring Security 「少管闲事」
    泛型就这么简单
    Spring Boot 对多线程支持提高程序执行效率
    Java桌面应用JavaFX01Hello World
    PostgreSQL实现Oracle merge into功能
    超大JSON文件解析方案(Java)
    记一次IDEA搭建Spring源码阅读环境
    Oracle转PostgreSQL之start with / connect by
  • 原文地址:https://www.cnblogs.com/zhangweizhong/p/12056118.html
Copyright © 2011-2022 走看看