队列和生产者消费者模型
一、队列Queue模块
- 用于IPC:进程之间的通信,具有同样功能的还有Manager模块
- 进程之间的通信可以用到的是队列和管道Pipe
- 队列是基于socket、pickle和锁来实现的,因为基于锁来实现,所以队列中的数据传输一定是安全的,但传输效率会有所损耗。
- 管道基于socket和pickle实现,但是没有加锁,所以管道传输效率高,数据却不安全。
- 多进程中的队列区别于普通的队列
- 导入:from multiprocessing import Queue
- 实例化对象:q = Queue()
- 往队列里传入数据:q.put(各种类型的数据)
从队列中取出数据:q.get(存入的数据)
队列中的数据遵循的是先进先出的原则,而栈遵循的是先进后出的原则。
from multiprocessing import Queue,Process
import os
def son(q):
q.put('你好')
# 往队列中添加消息:put,第一次存入数据
print(q.get())
# 从队列中获取消息:get()
def pro(q):
print(q.get())
# 接收第二个数据
if __name__ == '__main__':
q = Queue()
p = Process(target=son, args=(q,))
p.start()
p1 = Process(target=pro, args=(q,))
p1.start()
print(q.get())
# 接收第一个数据
q.put('hello')
q.put('第二个你好')
# 第二次存入数据
'''
结果是:
你好
hello
第二个你好
'''
二、生产者消费者模型
- 基本的概念:
- 生产者和消费者模型把原本完整的紧耦合数据处理过程拆分成松耦合多个数据处理部分,便于根据生产和消费的执行效率对数据处理过程中的不同部分进行修改规划生产者和消费者的个数,达到效率最大和平衡。
- 生产者:通过代码先获取数据,对数据进行加工和处理,处理完成之后把数据存入到队列之中
- 消费者:从队列中取出生产者加工处理之后的数据,还要进行某些操作
- 队列:队列Queue是消费者和生产者之间需要要用到的多进程通信的一种数据交换方式。
- 应用的场景:
- 在爬虫的时候对于网页的获取和网页的处理需要用到该模型
- 在分布式操作celery中也会用到,celery实际上就是两个大的生产者和消息者模型
- 该模型的本质:
让生产数据和消费数据的效率可以达到平衡并且效率最大化。
from multiprocessing import Queue, Process
import time
import random
def consumer(q):
for i in range(12):
print(q.get(i))
def producer(q):
for i in range(12):
q.put(i)
time.sleep(random.randint(1,3))
if __name__ == '__main__':
q = Queue()
c1 = Process(target=consumer, args=(q,))
p1 = Process(target=producer, args=(q,))
p1.start()
c1.start()
- 出现的问题及解决办法:
- 当生产者和消费者数量相等的情况下,很可能会出现生产者生产的速度跟不上消费者消费的速度,每生产一个商品,消费者会快速的吃掉。
'''版本一:生产者和消费者都是一个,或者数量一样,每生产一个,消费者快速的吃掉,这个会出现生产慢,消费快, 供不应求的情况 ''' from multiprocessing import Process, Queue import time import random def producer(q, name): for i in range(1,11): food = 'food%s' % i q.put(food) print('%s生产了商品%s' % (name, food)) time.sleep(1) def consumer(q, name): for i in range(1, 11): food = q.get() print('%s消费了商品%s' % (name, food)) time.sleep(1) if __name__ == '__main__': q = Queue() c1 = Process(target=consumer, args=(q,'小明')) p1 = Process(target=producer, args=(q,'刘洋')) p1.start() c1.start()
- 为了解决上边供不应求的情况,直接多开一个生产者进程,这个时候生产数量会增加一倍,但是消费者只能消费一半的供过于求的情况,如果再增加消费者就会出现供大于求和供不应求不断循环的情况。
'''版本二:为解决版本一的问题,版本二中添加一个生产者,扩大供应''' from multiprocessing import Process, Queue import time import random def producer1(q, name): for i in range(1,11): food = 'food%s' % i q.put(food) print('%s生产了商品%s' % (name, food)) time.sleep(1) def produce2(q, name): for i in range(1,11): food = 'food%s' % i q.put(food) print('%s生产了商品%s' % (name, food)) time.sleep(1) def consumer(q, name): for i in range(1, 11): food = q.get() print('%s消费了商品%s' % (name, food)) time.sleep(1) if __name__ == '__main__': q = Queue() c1 = Process(target=consumer, args=(q,'小明')) p1 = Process(target=producer1, args=(q,'刘洋')) p2 = Process(target=produce2, args=(q, '玲玲')) p1.start() p2.start() c1.start()
- 针对上述两个版本的问题,需要把消费者模型设置为无限循环,不停的消费,生产者模型设定生产的个数,生产完成后再向队列中传递一个None,当消费者获取到None的时候跳出循环,注意,这种方式下必须把异步设定为同步,即生产者必须先生产完,还有就是队列不能查询长度。
'''版本三:解决上述两个版本的问题:不设定消费者消费的数量,只设定生产者生产的数量''' from multiprocessing import Process, Queue import time import random def producer1(q, name): for i in range(1,11): food = 'food%s' % i q.put(food) print('%s生产了商品%s' % (name, food)) time.sleep(1) def produce2(q, name): for i in range(1,11): food = 'food%s' % i q.put(food) print('%s生产了商品%s' % (name, food)) time.sleep(1) def consumer(q, name): while True: food = q.get() # 注意:q.get()一个数据只能取一次,q.get()不能直接用于判断条件,需要赋值给变量 if food: # 存在消费 print('%s消费了商品%s' % (name, food)) time.sleep(1) else: # 如果不存在直接跳出循环 break if __name__ == '__main__': q = Queue() c1 = Process(target=consumer, args=(q,'小明')) p1 = Process(target=producer1, args=(q,'刘洋')) p2 = Process(target=produce2, args=(q, '玲玲')) p1.start() p2.start() p1.join() p2.join() # 添加join()让两个生产者生产完成 q.put(None) # 再向队列最后再添加一个None,因为队列遵循先进先出的原则,所以最后一个获取的是None c1.start()