zoukankan      html  css  js  c++  java
  • 生产者消费者模型应用场景及优势?

    在 工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。

    产 生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。

    在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商 品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模型。

    结构图如下:

    生产者消费者模型的优点:

    1、解耦

    假设生产者和消费者分别是两个类。

    如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。

    将来如果消费者的代码发生变化, 可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。

    举个例子,我们去邮局投递信件,如果不使用邮筒(也就是缓冲区),你必须得把信直接交给邮递员。

    有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须 得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了)。

    这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。

    万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。

    而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。

    2、支持并发

    由于生产者与消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,生产者只需要往缓冲区里丢数据,

    就可以继续生产下一个数据,而消费者只需要从缓冲区了拿数据即可,这样就不会因为彼此的处理速度而发生阻塞。

    接上面的例子,如果我们不使用邮筒,我们就得在邮局等邮递员,直到他回来,

    我们把信件交给他,这期间我们啥事儿都不能干(也就是生产者阻塞),或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。

    3、支持忙闲不均

    缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。

    当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。 等生产者的制造速度慢下来,消费者再慢慢处理掉。

    为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,

    需要寄出去的信超过1000封,这时 候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来 时再拿走。

    应用场景:

    使用多线程,在做爬虫的时候,生产者用着产生url链接,消费者用于获取url数据,在队列的帮助下可以使用多线程加快爬虫速度。

    import time
    import threading
    import Queue
    import urllib2
     
    class Consumer(threading.Thread):
      def __init__(self, queue):
        threading.Thread.__init__(self)
        self._queue = queue
     
      def run(self):
        while True:
          content = self._queue.get()
          print content
          if isinstance(content, str) and content == 'quit':
            break
          response = urllib2.urlopen(content)
        print 'Bye byes!'
     
     
    def Producer():
      urls = [
        'http://211.103.242.133:8080/Disease/Details.aspx?id=2258',
        'http://211.103.242.133:8080/Disease/Details.aspx?id=2258',
        'http://211.103.242.133:8080/Disease/Details.aspx?id=2258',
        'http://211.103.242.133:8080/Disease/Details.aspx?id=2258'
      ]
      queue = Queue.Queue()
      worker_threads = build_worker_pool(queue, 4)
      start_time = time.time()
      for url in urls:
        queue.put(url)
     
      for worker in worker_threads:
        queue.put('quit')
      for worker in worker_threads:
        worker.join()
     
      print 'Done! Time taken: {}'.format(time.time() - start_time)
     
     
    def build_worker_pool(queue, size):
      workers = []
      for _ in range(size):
        worker = Consumer(queue)
        worker.start()
        workers.append(worker)
      return workers
     
    if __name__ == '__main__':
      Producer()
  • 相关阅读:
    python返回列表最大值(java返回数组最大值)
    Mysql的5种索引添加类型
    阿里云中quick bi用地图分析数据时维度需转换为地理区域类型
    根据变量查找元素,并修改数值的实践
    Linux 通过命令设置网络
    mysql 实现 上一行减去下一行
    Spark 安装与启动
    Kafka 入门之集群部署遇到问题
    rmp使用方法
    Mysql 导入数据的一种方法
  • 原文地址:https://www.cnblogs.com/Rivend/p/12071128.html
Copyright © 2011-2022 走看看