理解生产者消费者模型及在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列表去重方法
Jan 17 Python
Python Socket编程详细介绍
Mar 23 Python
Python标准模块--ContextManager上下文管理器的具体用法
Nov 27 Python
python批量替换页眉页脚实例代码
Jan 22 Python
pandas string转dataframe的方法
Apr 11 Python
python 实现12bit灰度图像映射到8bit显示的方法
Jul 08 Python
详解Anconda环境下载python包的教程(图形界面+命令行+pycharm安装)
Nov 11 Python
如何获取Python简单for循环索引
Nov 21 Python
Jupyter notebook无法导入第三方模块的解决方式
Apr 15 Python
python datetime处理时间小结
Apr 16 Python
用Python 执行cmd命令
Dec 18 Python
装上这 14 个插件后,PyCharm 真的是无敌的存在
Jan 11 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
Snoopy类使用小例子
2008/04/15 PHP
php 远程关机操作的代码
2008/12/05 PHP
PHP 各种排序算法实现代码
2009/08/20 PHP
PHP常用开发函数解析之数组篇[未完结]
2012/07/30 PHP
php 获取本地IP代码
2013/06/23 PHP
php汉字转拼音的示例
2014/02/27 PHP
PHP实现WebService的简单示例和实现步骤
2015/03/27 PHP
Symfony2使用第三方库Upload制作图片上传实例详解
2016/02/04 PHP
总结一些PHP中好用但又容易忽略的小知识
2017/06/02 PHP
Laravel 实现在Blade模版中使用全局变量代替路径的例子
2019/10/22 PHP
juqery 学习之五 文档处理 插入
2011/02/11 Javascript
Jquery命名冲突解决的五种方案分享
2012/03/16 Javascript
js 实现在离开页面时提醒未保存的信息(减少用户重复操作)
2013/01/16 Javascript
JavaScript判断密码强度(自写代码)
2013/09/06 Javascript
jQuery Ajax调用WCF服务详细教程
2015/03/31 Javascript
jQuery固定元素插件scrolltofixed使用指南
2015/04/21 Javascript
jQuery使用drag效果实现自由拖拽div
2015/06/11 Javascript
JS实现的倒计时效果实例(2则实例)
2015/12/23 Javascript
使用Xcache缓存器加速PHP网站的配置方法
2017/04/22 Javascript
input框中自动展示当前日期yyyy/mm/dd的实现方法
2017/07/06 Javascript
Three.js基础学习之场景对象
2017/09/27 Javascript
vue-router路由懒加载和权限控制详解
2017/12/13 Javascript
vue调用语音播放的方法
2019/09/27 Javascript
Vue开发环境跨域访问问题
2020/01/22 Javascript
[01:00:49]DOTA2-DPC中国联赛 正赛 Ehome vs iG BO3 第二场 1月31日
2021/03/11 DOTA
python回调函数的使用方法
2014/01/23 Python
Python实现的HTTP并发测试完整示例
2020/04/23 Python
Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例
2017/08/31 Python
Python爬虫之Spider类用法简单介绍
2020/08/04 Python
详解pytorch tensor和ndarray转换相关总结
2020/09/03 Python
plt.figure()参数使用详解及运行演示
2021/01/08 Python
英国家庭家具、照明和花园家具购物网站:Furniture123
2018/12/31 全球购物
美国在线购物频道:Shop LC
2019/04/21 全球购物
幼儿园大班区域活动总结
2014/07/09 职场文书
工伤事故赔偿协议书
2014/10/27 职场文书
沂蒙六姐妹观后感
2015/06/08 职场文书