1、队列的介绍

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

创建队列的类(底层就是以管道和锁定的方式实现):

Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

参数介绍:

maxsize是队列中允许最大项数,省略则无大小限制。
但需要明确:
1、队列内存放的是消息而非大数据
2、队列占用的是内存空间,因而maxsize即便是无大小限制也受限于内存大小

主要方法:

q.put方法用以插入数据到队列中。
q.get方法可以从队列读取并且删除一个元素。
from multiprocessing import Process,Queue

q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(1)
q.put(2)
q.put(3)
print(q.full()) #满了
# q.put(4) #再放就阻塞住了 print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了
# print(q.get()) #再取就阻塞住了 True
1
2
3
True

二、生产者消费者模型

1、生产者消费者模型介绍

为什么要使用生产者消费者模型

生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,

如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,

才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。

为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者和消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

这个阻塞队列就是用来给生产者和消费者解耦的

2、生产者消费者模型实现

2.1 引入模型(生产一个消费一个)

import time
def producer():
for i in range(3):
res = f"包子 {i}"
time.sleep(0.5)
print(f"生产者生产了{res}")
consumer(res)
def consumer(res):
time.sleep(1)
print(f"消费者吃了{res}")
if __name__ == '__main__':
producer() 生产者生产了包子 0
消费者吃了包子 0
生产者生产了包子 1
消费者吃了包子 1
生产者生产了包子 2
消费者吃了包子 2

2.2 实现生产者消费者模型,但有小问题主进程永远不会结束

消费者不知道生产者已经完毕,一直处于等待状态,死循环

from multiprocessing import Process,Queue
import time
def producer(q):
for i in range(3):
res = f"包子 {i}"
time.sleep(0.5)
print(f"生产者生产了{res}")
# 把生产的给队列保存
q.put(res) def consumer(q):
while True:# 消费者一直接收
res = q.get()
time.sleep(1)
print(f"消费者吃了{res}")
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer,args=(q,))
p2 = Process(target=consumer,args=(q,))
p1.start()
p2.start()
print('主') 主
生产者生产了包子 0
生产者生产了包子 1
生产者生产了包子 2
消费者吃了包子 0
消费者吃了包子 1
消费者吃了包子 2

此时的问题是主进程永远不会结束,原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。

解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环

2.3、解决办法--低阶--生产者通知消费者生产结束

队列先进先出

from multiprocessing import Process,Queue
import time
def producer(q):
for i in range(3):
res = f"包子 {i}"
time.sleep(0.5)
print(f"生产者生产了{res}")
# 把生产的给队列保存
q.put(res) def consumer(q):
while True:# 消费者一直接收
res = q.get()
if res == None:
break
time.sleep(1)
print(f"消费者吃了{res}")
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer,args=(q,))
p2 = Process(target=consumer,args=(q,))
p1.start()
p2.start()
p1.join()# 主进程等待p1子进程执行完毕--即生产者生产完毕
q.put(None)
print('主') 生产者生产了包子 0
生产者生产了包子 1
生产者生产了包子 2
消费者吃了包子 0

消费者吃了包子 1
消费者吃了包子 2

但上述解决方式,在有多个生产者和多个消费者时,我们则需要用一个很low的方式去解决,有几个消费者就需要发送几次结束信号:相当low,例如

if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=(q,'egon1','包子'))
p2=Process(target=producer,args=(q,'egon2','骨头'))
p3=Process(target=producer,args=(q,'egon3','泔水')) #消费者们:即吃货们
c1=Process(target=consumer,args=(q,'alex1'))
c2=Process(target=consumer,args=(q,'alex2')) #开始
p1.start()
p2.start()
p3.start()
c1.start()
c2.start() p1.join()
p2.join()
p3.join()
q.put(None)
q.put(None)
q.put(None)
print('主')

2.4 JoinableQueue([maxsize]) 解决办法--高阶--消费者通知生生产者 项目已经被成功处理

这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。

JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常 q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
from multiprocessing import Process,JoinableQueue
import time
def producer(q):
for i in range(2):
res = f"包子 {i}"
time.sleep(0.5)
print(f"生产者生产了{res}")
# 把生产的给队列保存
q.put(res)
q.join()#等待消费者把自己放入队列中的所有的数据都取走之后,消费者才结束 def consumer(q):
while True:# 消费者一直接收
res = q.get()
# if res == None:
# break
time.sleep(1)
print(f"消费者吃了{res}")
q.task_done() #发送信号给q.join() 说明已经从队列中取走一个数据并处理完毕了
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer,args=(q,))
p2 = Process(target=producer,args=(q,))
p3 = Process(target=producer,args=(q,)) #------消费者--------
c1 = Process(target=consumer,args=(q,))
c2 = Process(target=consumer,args=(q,))
# 守护进程,消费者设置成守护进程,主进程结束完后 消费者 结束
c1.daemon = True
c2.daemon = True p1.start()
p2.start()
p3.start() c1.start()
c2.start() p1.join()
p2.join()
p3.join() print('主')

3、总结

1、程序中有两类角色

一类负责生产数据(生产者)
一类负责处理数据(消费者)

2、引入生产者消费者模型为了解决的问题是

平衡生产者与消费者之间的速度差
程序解开耦合

3、如何实现生产者消费者模型

生产者<--->队列<--->消费者


5 并发编程-(进程)-队列&生产者消费者模型的更多相关文章

  1. 【Java并发编程】:生产者—消费者模型

    生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. 这里实现如下情况的生产--消费模型: 生产者不断交替地生产两组数据“姓 ...

  2. #queue队列 #生产者消费者模型

    #queue队列 #生产者消费者模型 #queue队列 #有顺序的容器 #程序解耦 #提高运行效率 #class queue.Queue(maxsize=0) #先入先出 #class queue.L ...

  3. python网络编程--进程(方法和通信),锁, 队列,生产者消费者模型

    1.进程 正在进行的一个过程或者说一个任务.负责执行任务的是cpu 进程(Process: 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在 ...

  4. python 全栈开发,Day39(进程同步控制(锁,信号量,事件),进程间通信(队列,生产者消费者模型))

    昨日内容回顾 python中启动子进程并发编程并发 :多段程序看起来是同时运行的ftp 网盘不支持并发socketserver 多进程 并发异步 两个进程 分别做不同的事情 创建新进程join :阻塞 ...

  5. python 进程锁 生产者消费者模型 队列 (进程其他方法,守护进程,数据共享,进程隔离验证)

    #######################总结######### 主要理解 锁      生产者消费者模型 解耦用的   队列 共享资源的时候 是不安全的 所以用到后面的锁 守护进程:p.daem ...

  6. Learning-Python【34】:进程之生产者消费者模型

    一.什么是生产者消费者模型 生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据.同样 ...

  7. Day034--Python--锁, 信号量, 事件, 队列, 生产者消费者模型, joinableQueue

    进程同步: 1. 锁 (重点)    锁通常被用来实现对共享资源的同步访问.为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁, ...

  8. python2.0_s12_day9之day8遗留知识(queue队列&生产者消费者模型)

    4.线程 1.语法 2.join 3.线程锁之Lock\Rlock\信号量 4.将线程变为守护进程 5.Event事件 * 6.queue队列 * 7.生产者消费者模型 4.6 queue队列 que ...

  9. 队列&生产者消费者模型

    队列 ipc机制:进程通讯 管道:pipe 基于共享的内存空间 队列:pipe+锁 queue from multiprocessing import Process,Queue ### 案例一 q ...

随机推荐

  1. asp.net 导出excel的一种方法

    项目用到的一种导出excel 的方法予以记录:(具体的业务类可更具情况替换使用) protected void Export(string filename, List<ComponentCon ...

  2. MSMQ-发送消息到远程专用队列path格式

    在工作组模式下,远程访问专用队列.在网上找到一篇文章,翻译了一下. 最后结论,直接使用多元素格式名方式,利用IP地址直接对单个或多个目标发送消息      MessageQueue rmQ = new ...

  3. FastAdmin 插件的 Git 开发流程(简明)

    FastAdmin 插件的 Git 开发流程(简明) cms zip 安装 包安装 删除 addons 里的 cms 使用 mklink 软链接到 cms 插件 Git 仓库 修改 cms 插件 gi ...

  4. RabbitMQ之监控

    RabbitMQ作为一个工业级的消息中间件,肯定是缺少不了监控的,RabbitMQ提供了WEB版的页面监控(访问地址:http://xxx.xxx.xxx.xxx:15672/,默认端口号是15672 ...

  5. java 设计模式:单例模式

    Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯 ...

  6. php 生成.csv的文件

    $data = array( "title" => array("服务器", "链接", "对应ID"), &qu ...

  7. 1053 Path of Equal Weight (30 分)

    Given a non-empty tree with root R, and with weight W​i​​ assigned to each tree node T​i​​. The weig ...

  8. mysql互为主从

    摘自:http://flash520.blog.163.com/blog/static/3441447520101029114016823/ A B 为两台MySQL服务器,均开启二进制日志,数据库版 ...

  9. Logistic回归的两种形式y=0/1,y=+1/-1

    第一种形式:y=0/1 第二种形式:y=+1/-1 第一种形式的损失函数可由极大似然估计推出: 第二种形式的损失函数:  , 参考:https://en.wikipedia.org/wiki/Loss ...

  10. [ffmpeg]deocde audio(v3.3.2)

    /* * Copyright (c) 2001 Fabrice Bellard * * Permission is hereby granted, free of charge, to any per ...