理解生产者消费者模型及在Python编程中的运用实例


Posted in Python onJune 26, 2016

什么是生产者消费者模型

在 工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产 生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商 品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模型。结构图如下:

理解生产者消费者模型及在Python编程中的运用实例

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

1、解耦

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

举个例子,我们去邮局投递信件,如果不使用邮筒(也就是缓冲区),你必须得把信直接交给邮递员。有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须 得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了)。这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。

2、支持并发

由于生产者与消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,生产者只需要往缓冲区里丢数据,就可以继续生产下一个数据,而消费者只需要从缓冲区了拿数据即可,这样就不会因为彼此的处理速度而发生阻塞。

接上面的例子,如果我们不使用邮筒,我们就得在邮局等邮递员,直到他回来,我们把信件交给他,这期间我们啥事儿都不能干(也就是生产者阻塞),或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。

3、支持忙闲不均

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

为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000封,这时 候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来 时再拿走。

Python示例:
利用队列实现简单的生产者消费者模型,生产者产生时间放入队列,消费者取出时间打印

class Consumer(threading.Thread):
  def __init__(self, queue):
    threading.Thread.__init__(self)
    self._queue = queue

  def run(self):
    while True:
      msg = self._queue.get()
      if isinstance(msg, str) and msg == 'quit':
        break
      print "I'm a thread, and I received %s!!" % msg
    print 'Bye byes!'


def producer():
  queue = Queue.Queue()
  worker = Consumer(queue)
  worker.start() # 开启消费者线程
  start_time = time.time()
  while time.time() - start_time < 5:
    queue.put('something at %s' % time.time())
    time.sleep(1)
  queue.put('quit')
  worker.join()


if __name__ == '__main__':
  producer()

   
使用多线程,在做爬虫的时候,生产者用着产生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 相关文章推荐
Python和perl实现批量对目录下电子书文件重命名的代码分享
Nov 21 Python
Python 抓取动态网页内容方案详解
Dec 25 Python
Python numpy 提取矩阵的某一行或某一列的实例
Apr 03 Python
dataframe设置两个条件取值的实例
Apr 12 Python
Python基于递归算法实现的汉诺塔与Fibonacci数列示例
Apr 18 Python
浅析python中numpy包中的argsort函数的使用
Aug 30 Python
python实现排序算法解析
Sep 08 Python
TensorFlow2.0:张量的合并与分割实例
Jan 19 Python
tensorflow保持每次训练结果一致的简单实现
Feb 17 Python
解决python -m pip install --upgrade pip 升级不成功问题
Mar 05 Python
Python使用Turtle模块绘制国旗的方法示例
Feb 28 Python
教你怎么用Python selenium操作浏览器对象的基础API
Jun 23 Python
python安装mysql-python简明笔记(ubuntu环境)
Jun 25 #Python
Python的装饰器用法学习笔记
Jun 24 #Python
Python的网络编程库Gevent的安装及使用技巧
Jun 24 #Python
深入解析Python编程中super关键字的用法
Jun 24 #Python
深入了解Python数据类型之列表
Jun 24 #Python
Python实现信用卡系统(支持购物、转账、存取钱)
Jun 24 #Python
Python提取Linux内核源代码的目录结构实现方法
Jun 24 #Python
You might like
Codeigniter上传图片出现“You did not select a file to upload”错误解决办法
2014/06/12 PHP
PHP中的traits简单使用实例
2015/05/13 PHP
PHP微信支付开发实例
2016/06/22 PHP
php从数据库中读取特定的行(实例)
2017/06/02 PHP
jquery easyui的tabs使用时的问题
2010/03/23 Javascript
根据一段代码浅谈Javascript闭包
2010/12/14 Javascript
js编码之encodeURIComponent使用介绍(asp,php)
2012/03/01 Javascript
纯JS实现五子棋游戏兼容各浏览器(附源码)
2013/04/24 Javascript
一个css与js结合的下拉菜单支持主流浏览器
2014/10/08 Javascript
js构造函数、索引数组和属性的实现方式和使用
2014/11/16 Javascript
个人总结的一些JavaScript技巧、实用函数、简洁方法、编程细节
2015/06/10 Javascript
JavaScript sort数组排序方法和自我实现排序方法小结
2016/06/06 Javascript
AngularJS基础 ng-non-bindable 指令详细介绍
2016/08/02 Javascript
node.js中 stream使用教程
2016/08/28 Javascript
bootstrapValidator表单验证插件学习
2016/12/30 Javascript
node.js利用redis数据库缓存数据的方法
2017/03/01 Javascript
详解vue 模版组件的三种用法
2017/07/21 Javascript
微信小程序页面生命周期详解
2018/01/31 Javascript
详解angular2.x创建项目入门指令
2018/10/11 Javascript
微信小程序人脸识别功能代码实例
2019/05/07 Javascript
手把手带你入门微信小程序新框架Kbone的使用
2020/02/25 Javascript
Node Mongoose用法详解【Mongoose使用、Schema、对象、model文档等】
2020/05/13 Javascript
浅析PHP与Python进行数据交互
2018/05/15 Python
Python3删除排序数组中重复项的方法分析
2019/01/31 Python
新手入门学习python Numpy基础操作
2020/03/02 Python
一篇文章搞懂python的转义字符及用法
2020/09/03 Python
ProBikeKit新西兰:自行车套件,跑步和铁人三项装备
2017/04/05 全球购物
Book Depository亚太地区:一家领先的国际图书零售商
2019/05/05 全球购物
澳大利亚人信任的清洁平台,您的私人管家:Jarvis
2020/12/25 全球购物
什么是组件架构
2016/05/15 面试题
幼儿发展评估方案
2014/06/11 职场文书
试用期自我评价范文
2015/03/10 职场文书
2015年乡镇卫生院妇幼保健工作总结
2015/05/19 职场文书
黑白记忆观后感
2015/06/18 职场文书
HTML+css盒子模型案例(圆,半圆等)“border-radius” 简单易上手
2021/05/10 HTML / CSS
Linux、ubuntu系统下查看显卡型号、显卡信息详解
2022/04/07 Servers