理解生产者消费者模型及在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()
(0)

相关推荐

  • 用Python的SimPy库简化复杂的编程模型的介绍

    在我遇到 SimPy 包的其中一位创始人 Klaus Miller 时,从他那里知道了这个包.Miller 博士阅读过几篇提出使用 Python 2.2+ 生成器实现半协同例程和"轻便"线程的技术的 可爱的 Python专栏文章.特别是(使我很高兴的是),他发现在用 Python 实现 Simula-67 样式模拟时,这些技术很有用. 结果表明 Tony Vignaux 和 Chang Chui 以前曾创建了另一个 Python 库,它在概念上更接近于 Simscript,而且该库使用

  • 基于python yield机制的异步操作同步化编程模型

    本文总结下如何在编写python代码时对异步操作进行同步化模拟,从而提高代码的可读性和可扩展性. 游戏引擎一般都采用分布式框架,通过一定的策略来均衡服务器集群的资源负载,从而保证服务器运算的高并发性和CPU高利用率,最终提高游戏的性能和负载.由于引擎的逻辑层调用是非抢占式的,服务器之间都是通过异步调用来进行通讯,导致游戏逻辑无法同步执行,所以在代码层不得不人为地添加很多回调函数,使一个原本完整的功能碎片化地分布在各个回调函数中. 异步逻辑 以游戏中的副本评分逻辑为例,在副本结束时副本管理进程需要

  • 用Python给文本创立向量空间模型的教程

    我们需要开始思考如何将文本集合转化为可量化的东西.最简单的方法是考虑词频. 我将尽量尝试不使用NLTK和Scikits-Learn包.我们首先使用Python讲解一些基本概念. 基本词频 首先,我们回顾一下如何得到每篇文档中的词的个数:一个词频向量. #examples taken from here: http://stackoverflow.com/a/1750187 mydoclist = ['Julie loves me more than Linda loves me', 'Jane

  • python基于隐马尔可夫模型实现中文拼音输入

    在网上看到一篇关于隐马尔科夫模型的介绍,觉得简直不能再神奇,又在网上找到大神的一篇关于如何用隐马尔可夫模型实现中文拼音输入的博客,无奈大神没给可以运行的代码,只能纯手动网上找到了结巴分词的词库,根据此训练得出隐马尔科夫模型,用维特比算法实现了一个简单的拼音输入法.githuh地址:https://github.com/LiuRoy/Pinyin_Demo 原理简介隐马尔科夫模型 抄一段网上的定义: 隐马尔可夫模型 (Hidden Markov Model) 是一种统计模型,用来描述一个含有隐含未

  • Python探索之pLSA实现代码

    pLSA(probabilistic Latent Semantic Analysis),概率潜在语义分析模型,是1999年Hoffman提出的一个被称为第一个能解决一词多义问题的模型,通过在文档与单词之间建立一层主题(Topic),将文档与单词的直接关联转化为文档与主题的关联以及主题与单词的关联.这里采用EM算法进行估计,可能存在差错,望积极批评指正. # -*- coding: utf-8 -*- import math import random import jieba import c

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

    什么是生产者消费者模型 在 工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产 生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者.在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商 品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模型.结构图如下: 生产者消费者模型的优点: 1.解耦 假设生产者和消费者分别是两个类.如果让生产者直接调用消费者的某个方法,

  • Python之两种模式的生产者消费者模型详解

    第一种使用queue队列实现: #生产者消费者模型 其实服务器集群就是这个模型 # 这里介绍的是非yield方法实现过程 import threading,time import queue q = queue.Queue(maxsize=10) def Producer(anme): # for i in range(10): # q.put('骨头%s'%i) count = 1 while True: q.put('骨头%s'%count) print('生产了骨头',count) cou

  • Python生成器实现简单"生产者消费者"模型代码实例

    生成器定义 在Python中,一边循环一边计算的机制,称为生成器:generator. 为什么要有生成器 列表所有数据都在内存中,如果有海量数据的话将会非常耗内存. 如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了. 如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间. 简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器! 使用生成器实现简单的生产者消费者模型 1.效果截屏 代

  • python之生产者消费者模型实现详解

    代码及注释如下 #Auther Bob #--*--conding:utf-8 --*-- #生产者消费者模型,这里的例子是这样的,有一个厨师在做包子,有一个顾客在吃包子,有一个服务员在储存包子,这个服务员我们就可以用queue来实现 import threading import queue import time ''' def consumer(p,que): id = que.get() print("[%s]来吃包子了,我吃到的包子的名字是[%s]" %(p,id)) def

  • 生产者消费者模型ThreadLocal原理及实例详解

    1.生产者消费者模型作用和示例如下: 1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率 ,这是生产者消费者模型最重要的作用 2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约 备注:对于生产者消费者模型的理解将在并发队列BlockingQueue章节进行说明,本章不做详细介绍. package threadLearning.productCustomerModel; /* wait/notify 机制

  • Queue 实现生产者消费者模型(实例讲解)

    Python中,队列是线程间最常用的交换数据的形式. Python Queue模块有三种队列及构造函数: 1.Python Queue模块的FIFO队列先进先出. class Queue.Queue(maxsize) 2.LIFO类似于堆,即先进后出. class Queue.LifoQueue(maxsize) 3.还有一种是优先级队列级别越低越先出来. class Queue.PriorityQueue(maxsize) 此包中的常用方法(q = Queue.Queue()): q.qsiz

  • 浅谈生产者消费者模型(Linux系统下的两种实现方法)

    生产者消费者问题是同步问题中的一种常见情况,借用一下维基百科的话 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例.该问题描述了两个共享固定大小缓冲区的线程--即所谓的"生产者"和"消费者"--在实际运行时会发生的问题.生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程.与此同时,消费者也在缓冲区消耗这些数据.该问题的关键就

  • Java基于Lock的生产者消费者模型示例

    本文实例讲述了Java基于Lock的生产者消费者模型.分享给大家供大家参考,具体如下: 前面一篇<Java锁机制Lock用法>简单介绍了锁机制,这里进一步分析一下基于lock的生产者消费者模型. package com.expgiga.JUC; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Reentra

  • Java多线程 生产者消费者模型实例详解

    生产者消费者模型 生产者:生产任务的个体: 消费者:消费任务的个体: 缓冲区:是生产者和消费者之间的媒介,对生产者和消费者解耦. 当 缓冲区元素为满,生产者无法生产,消费者继续消费: 缓冲区元素为空,消费者无法消费,生产者继续生产: wait()/notify()生产者消费者模型 制作一个简单的缓冲区ValueObject,value为空表示缓冲区为空,value不为空表示缓冲区满 public class ValueObject { public static String value = "

  • Java多线程 BlockingQueue实现生产者消费者模型详解

    BlockingQueue BlockingQueue.解决了多线程中,如何高效安全"传输"数据的问题.程序员无需关心什么时候阻塞线程,什么时候唤醒线程,该唤醒哪个线程. 方法介绍 BlockingQueue是Queue的子类 void put(E e) 插入指定元素,当BlockingQueue为满,则线程阻塞,进入Waiting状态,直到BlockingQueue有空闲空间再继续. 这里以ArrayBlockingQueue为例进行分析 void take() 队首出队,当Bloc

随机推荐