Python进程间通信 multiProcessing Queue队列实现详解


Posted in Python onSeptember 23, 2019

一、进程间通信

IPC(Inter-Process Communication)

IPC机制:实现进程之间通讯

管道:pipe 基于共享的内存空间

队列:pipe+锁的概念--->queue

二、队列(Queue)

2.1 概念-----multiProcess.Queue

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

Queue([maxsize])创建共享的进程队列。

参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。

底层队列使用管道和锁定实现。

2.2 Queue方法使用

2.2.1 q.get的使用:

是从队列里面取值并且把队列面的取出来的值删掉,没有参数的情况下就是是默认一直等着取值

就算是队列里面没有可取的值的时候,程序也不会结束,就会卡在哪里,一直等着

from multiprocessing import Queue
q = Queue() # 生成一个队列对象
# put方法是往队列里面放值
q.put('Cecilia陈')
q.put('xuchen')
q.put('喜陈')

# get方法是从队列里面取值
print(q.get())
print(q.get())
print(q.get())

q.put(5)
q.put(6)
print(q.get())

Cecilia陈

xuchen

喜陈

5

2.2.2 Queue(参数) +参数的使用:

Queue加参数以后,参数是数值

参数实几就表示实例化的这个Queue队列可以放几个值

当队列已经满的时候,再放值,程序会阻塞,但不会结束

from multiprocessing import Queue
q = Queue(3)
q.put('Cecilia陈')
q.put('xuchen')
q.put('喜陈')
print(q.full()) # 判断队列是否满了 返回的是True/False
q.put(2) # 当队列已经满的时候,再放值,程序会阻塞,但不会结束

True 队列已经满了

2.2.3 q.put(参数1,参数2,参数3,参数4):

q.put(self, obj, block=True, timeout=None)

self :put就相当于是Queue里的一个方法,这个时候q.put就相当于是队列对象q来调用对象的绑定方法,这个参数可以省略即可

obj:是我们需要往队列里面放的值

block=True :队列如果满了的话,再往队列里放值的话会等待,程序不会结束

timeout=None:是再block这个参数的基础上的,当block的值为真的时候,timeout是用来等待多少秒,如果再这个时间里,队列一直是满的,那么程序就会报错并结束(Queue.Full异常)

from multiprocessing import Queue
q = Queue(3)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=5) # 此时程序将对等待5秒以后报错了

2.2.4 q.get(参数1,参数2,参数3,参数4):

q.get(self,block=True, timeout=None)

self :get就相当于是Queue里的一个方法,这个时候q.get就相当于是队列对象q来调用对象的绑定方法,这个参数可以省略即可

block=True :从队列q对象里面取值,如果娶不到值的话,程序不会结束

timeout=None:是再block这个参数的基础上的,当block的值为真的时候,timeout是用来等待多少秒,如果再这个时间里,get取不到队列里面的值的话,那么程序就会报错并结束(queue.Empty异常)

from multiprocessing import Queue
q = Queue()
q.put('Cecilia陈')
print(q.get())
q.get(block=True,timeout=2) # 此时程序会等待2秒后,报错了,队列里面没有值了

2.2.5 block=False:

如果block的值是False的话,那么put方法再队列是满的情况下,不会等待阻塞,程序直接报错(Queue.Full异常)结束

如果block的值是False的话,那么get方法再队列里面没有值的情况下,再去取的时候,不会等待阻塞,程序直接报错(queue.Empty异常)结束

1.put()的block=False

from multiprocessing import Queue
q = Queue(2)
q.put('Cecilia陈')
q.put('喜陈')
print(q.full())
q.put('xichen',block=False) # 队列已经满了,我不等待了,直接报错

2.get()的block=Flase

from multiprocessing import Queue
q = Queue(2)
q.put('Cecilia陈')
q.put('喜陈')
print(q.get())
print(q.get())
print(q.get(block=False)) # 队列已经没有值了,我不等待了,直接报错

2.2.6 put_nowait()/get_nowait()

1.put_nowait() 相当于bolok=False,队列满的时候,再放值的时候,程序不等待,不阻塞,直接报错

from multiprocessing import Queue
q = Queue(2)
q.put('Cecilia陈')
q.put('喜陈')
print(q.full())

q.put_nowait('xichen') # 程序不等待,不阻塞,直接报错

2.get_nowait() 相当于bolok=False,当队列里没有值的时候,再取值的时候,程序不等待,不阻塞,程序直接报错

from multiprocessing import Queue
q = Queue(2)
q.put('Cecilia陈')
q.put('喜陈')
print(q.get())
print(q.get())
print(q.full())
q.get_nowait()# 再取值的时候,程序不等待,不阻塞,程序直接报错

三、代码实例

3.1 单看队列的存取数据用法

这个例子还没有加入进程通信,只是先来看看队列为我们提供的方法,以及这些方法的使用和现象。

'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''

from multiprocessing import Queue
q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
# q.put(3)  # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
      # 如果队列中的数据一直不被取走,程序就会永远停在这里。
try:
  q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
  print('队列已经满了')

# 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
print(q.full()) #满了
print(q.get())
print(q.get())
print(q.get())
# print(q.get()) # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
try:
  q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
  print('队列已经空了')

print(q.empty()) #空了

3.2 子进程向父进程发送数据

这是一个queue的简单应用,使用队列q对象调用get函数来取得队列中最先进入的数据。

from multiprocessing import Process, Queue
def f(q,name,age):
  q.put(name,age) #调用主函数中p进程传递过来的进程参数 put函数为向队列中添加一条数据。
if __name__ == '__main__':
  q = Queue() #创建一个Queue对象
  p = Process(target=f, args=(q,'Cecilia陈',18)) #创建一个进程
  p.start()
  print(q.get())
  p.join()

['Cecilia陈', 18]

四、生产者消费者模型

生产者: 生产数据的任务

消费者: 处理数据的任务

生产者--队列(盆)-->消费者

生产者可以不停的生产,达到了自己最大的生产效率,消费者可以不停的消费,也达到了自己最大的消费效率.

生产者消费者模型大大提高了生产者生产的效率和消费者消费的效率.

补充: queue不适合传大文件,通产传一些消息.

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

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

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

4.2 什么是生产者消费者模型

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

4.3 基于Queue队列实现的生产者消费者模型

from multiprocessing import Queue,Process
# 生产者
def producer(q,name,food):
  for i in range(3):
    print(f'{name}生产了{food}{i}')
    res = f'{food}{i}'
    q.put(res)
# 消费者
def consumer(q,name):
  while True:
    res = q.get(timeout=5)
    print(f'{name}吃了{res}')
if __name__ == '__main__':
  q = Queue() # 为的是让生产者和消费者使用同一个队列,使用同一个队列进行通讯
  p1 = Process(target=producer,args=(q,'Cecilia陈','巧克力'))
  c1 = Process(target=consumer,args=(q,'Tom'))
  p1.start()
  c1.start()

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

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

4.4 改良版----生产者消费者模型

注意:结束信号None,不一定要由生产者发,主进程里同样可以发,但主进程需要等生产者结束后才应该发送该信号

from multiprocessing import Queue,Process
def producer(q,name,food):
  for i in range(3):
    print(f'{name}生产了{food}{i}')
    res = f'{food}{i}'
    q.put(res)
  q.put(None) # 当生产者结束生产的的时候,我们再队列的最后再做一个表示,告诉消费者,生产者已经不生产了,让消费者不要再去队列里拿东西了
def consumer(q,name):
  while True:
    res = q.get(timeout=5)
    if res == None:break # 判断队列拿出的是不是生产者放的结束生产的标识,如果是则不取,直接退出,结束程序
    print(f'{name}吃了{res}')
if __name__ == '__main__':
  q = Queue() # 为的是让生产者和消费者使用同一个队列,使用同一个队列进行通讯
  p1 = Process(target=producer,args=(q,'Cecilia陈','巧克力'))
  c1 = Process(target=consumer,args=(q,'Tom'))
  p1.start()
  c1.start()

4.5 主进程在生产者生产结束以后,发送结束信号

使用这个方法的话,是很low的,有几个消费者就要在主进程中向队列中put几个结束信号

from multiprocessing import Queue,Process
import time,random

def producer(q,name,food):
  for i in range(3):
    print(f'{name}生产了{food}{i}')
    time.sleep((random.randint(1,3)))
    res = f'{food}{i}'
    q.put(res)
  # q.put(None) # 当生产者结束生产的的时候,我们再队列的最后再做一个表示,告诉消费者,生产者已经不生产了,让消费者不要再去队列里拿东西了



def consumer(q,name):
  while True:
    res = q.get(timeout=5)
    if res == None:break # 判断队列拿出的是不是生产者放的结束生产的标识,如果是则不取,直接退出,结束程序
    time.sleep((random.randint(1, 3)))
    print(f'{name}吃了{res}')

if __name__ == '__main__':
  q = Queue() # 为的是让生产者和消费者使用同一个队列,使用同一个队列进行通讯
  # 多个生产者进程
  p1 = Process(target=producer,args=(q,'Cecilia陈','巧克力'))
  p2 = Process(target=producer,args=(q,'xichen','冰激凌'))
  p3 = Process(target=producer,args=(q,'喜陈','可乐'))
  # 多个消费者进程
  c1 = Process(target=consumer,args=(q,'Tom'))
  c2 = Process(target=consumer,args=(q,'jack'))


  # 告诉操作系统启动生产者进程
  p1.start()
  p2.start()
  p3.start()

  # 告诉操作系统启动消费者进程
  c1.start()
  c2.start()

  p1.join()
  p2.join()
  p3.join()

  q.put(None) # 几个消费者put几次
  q.put(None)

五、JoinableQueue方法

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

5.1 方法介绍

JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:

q.task_done():使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。

q.join():生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。

5.2 joinableQueue队列实现生产者消费者模型

from multiprocessing import Queue,Process,JoinableQueue
import time,random

def producer(q,name,food):
  for i in range(3):
    print(f'{name}生产了{food}{i}')
    # time.sleep((random.randint(1,3)))
    res = f'{food}{i}'
    q.put(res)
  # q.put(None) # 当生产者结束生产的的时候,我们再队列的最后再做一个表示,告诉消费者,生产者已经不生产了,让消费者不要再去队列里拿东西了
  q.join()


def consumer(q,name):
  while True:
    res = q.get(timeout=5)
    # if res == None:break # 判断队列拿出的是不是生产者放的结束生产的标识,如果是则不取,直接退出,结束程序
    # time.sleep((random.randint(1, 3)))
    print(f'{name}吃了{res}')
    q.task_done()#向q.join()发送一次信号,证明一个数据已经被取走了


if __name__ == '__main__':
  q = JoinableQueue() # 为的是让生产者和消费者使用同一个队列,使用同一个队列进行通讯
  # 多个生产者进程
  p1 = Process(target=producer,args=(q,'Cecilia陈','巧克力'))
  p2 = Process(target=producer,args=(q,'xichen','冰激凌'))
  p3 = Process(target=producer,args=(q,'喜陈','可乐'))
  # 多个消费者进程
  c1 = Process(target=consumer,args=(q,'Tom'))
  c2 = Process(target=consumer,args=(q,'jack'))


  # 告诉操作系统启动生产者进程
  p1.start()
  p2.start()
  p3.start()

  # 把生产者设为守护进程
  c1.daemon = True
  c2.daemon = True
  # 告诉操作系统启动消费者进程
  c1.start()
  c2.start()

  p1.join()
  p2.join()
  p3.join() # 等待生产者生产完毕

  print('主进程')

  ### 分析
  # 生产者生产完毕--这是主进程最后一行代码结束--q.join()消费者已经取干净了,没有存在的意义了
  # 这是主进程最后一行代码结束,消费者已经取干净了,没有存在的意义了.守护进程的概念.

5.3 测试joinableQueue

from multiprocessing import Process,Queue,JoinableQueue
q = JoinableQueue()
q.put('zhao') # 放队列里一个任务
q.put('qian')
print(q.get())
q.task_done() # 完成了一次任务
print(q.get())
q.task_done() # 完成了一次任务
q.join() #计数器不为0的时候 阻塞等待计数器为0后通过

# 想象成一个计数器 :put +1  task_done -1

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python获取标准北京时间的方法
Mar 24 Python
详解Python中内置的NotImplemented类型的用法
Mar 31 Python
python uuid模块使用实例
Apr 08 Python
pip命令无法使用的解决方法
Jun 12 Python
Python3实现将本地JSON大数据文件写入MySQL数据库的方法
Jun 13 Python
Django中的Model操作表的实现
Jul 24 Python
对web.py设置favicon.ico的方法详解
Dec 04 Python
OpenCV HSV颜色识别及HSV基本颜色分量范围
Mar 22 Python
Matplotlib中%matplotlib inline如何使用
Jul 28 Python
Python3.9.1中使用match方法详解
Feb 08 Python
Python 中 Shutil 模块详情
Nov 11 Python
Python数据可视化之Seaborn的安装及使用
Apr 19 Python
python程序中的线程操作 concurrent模块使用详解
Sep 23 #Python
Python3 pandas 操作列表实例详解
Sep 23 #Python
详解基于python-django框架的支付宝支付案例
Sep 23 #Python
如何利用Python开发一个简单的猜数字游戏
Sep 22 #Python
Python中关于浮点数的冷知识
Sep 22 #Python
Python安装及Pycharm安装使用教程图解
Sep 20 #Python
Python实现语音识别和语音合成功能
Sep 20 #Python
You might like
PHP+FastCGI+Nginx配置PHP运行环境
2014/08/07 PHP
yii2.0框架场景的简单使用示例
2020/01/25 PHP
jQuery对象和DOM对象使用说明
2010/06/25 Javascript
JavaScript中去掉数组中的重复值的实现方法
2011/08/03 Javascript
浅析JavaScript中的typeof运算符
2013/11/30 Javascript
div浮层,滚动条移动,位置保持不变的4种方法汇总
2013/12/11 Javascript
浅谈js的ajax的异步和同步请求的问题
2016/10/07 Javascript
JQuery实现动态操作表格
2017/01/11 Javascript
Node.js v8.0.0正式发布!看看带来了哪些主要新特性
2017/06/02 Javascript
关于在mongoose中填充外键的方法详解
2017/08/14 Javascript
vue组件实现进度条效果
2018/06/06 Javascript
Typescript 中的 interface 和 type 到底有什么区别详解
2019/06/18 Javascript
原生JavaScript实现日历功能代码实例(无引用Jq)
2019/09/23 Javascript
[41:12]Liquid vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.24
2019/09/10 DOTA
关于Python元祖,列表,字典,集合的比较
2017/01/06 Python
python实现决策树
2017/12/21 Python
Django代码性能优化与Pycharm Profile使用详解
2018/08/26 Python
Tensorflow使用支持向量机拟合线性回归
2018/09/07 Python
Python学习笔记之图片人脸检测识别实例教程
2019/03/06 Python
Python 给屏幕打印信息加上颜色的实现方法
2019/04/24 Python
python 根据字典的键值进行排序的方法
2019/07/24 Python
python操作excel让工作自动化
2019/08/09 Python
使用Python pip怎么升级pip
2020/08/11 Python
scrapy在python爬虫中搭建出错的解决方法
2020/11/22 Python
HTML5 新标签全部总汇(推荐)
2016/06/13 HTML / CSS
荷兰鞋子在线:Nelson Schoenen
2017/12/25 全球购物
中秋节礼品促销方案
2014/02/02 职场文书
党员岗位承诺口号大全
2014/03/28 职场文书
社区健康教育工作方案
2014/06/03 职场文书
办公室禁烟通知
2015/04/23 职场文书
《司马光》教学反思
2016/02/22 职场文书
浙江省杭州市平均工资标准是多少?
2019/07/09 职场文书
Python将CSV文件转化为HTML文件的操作方法
2021/06/30 Python
winserver2019安装软件一直卡在应用程序正在为首次使用做准备
2022/06/10 Servers
Python自动操作神器PyAutoGUI的使用教程
2022/06/16 Python
Win11运行cmd提示“请求的操作需要提升”的两种解决方法
2022/07/07 数码科技