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实现基于HTTP文件传输实例
Nov 08 Python
Python中的匿名函数使用简介
Apr 27 Python
分析Python中设计模式之Decorator装饰器模式的要点
Mar 02 Python
Python爬虫之xlml解析库(全面了解)
Aug 08 Python
python3模块smtplib实现发送邮件功能
May 22 Python
pandas将numpy数组写入到csv的实例
Jul 04 Python
python设定并获取socket超时时间的方法
Jan 12 Python
Python开启线程,在函数中开线程的实例
Feb 22 Python
django 配置阿里云OSS存储media文件的例子
Aug 20 Python
Python列表list常用内建函数实例小结
Oct 22 Python
解决pycharm导入本地py文件时,模块下方出现红色波浪线的问题
Jun 01 Python
Python configparser模块封装及构造配置文件
Aug 07 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中获取文件扩展名的N种方法小结
2012/02/27 PHP
php中AES加密解密的例子小结
2014/02/18 PHP
完善CodeIgniter在IDE中代码提示功能的方法
2014/07/19 PHP
重新认识php array_merge函数
2014/08/31 PHP
WordPress中"无法将上传的文件移动至"错误的解决方法
2015/07/01 PHP
PHP中利用sleep函数实现定时执行功能实现代码
2016/08/25 PHP
php实现的PDO异常处理操作分析
2018/12/27 PHP
JavaScript中的私有成员
2006/09/18 Javascript
IE和FireFox(FF)中js和css的不同
2009/04/13 Javascript
js不完美解决click和dblclick事件冲突问题
2012/07/16 Javascript
js动态创建标签示例代码
2014/06/09 Javascript
JavaScript中逗号运算符介绍及使用示例
2015/03/13 Javascript
js鼠标跟随运动效果
2017/03/11 Javascript
JS实现运动缓冲效果的封装函数示例
2018/02/18 Javascript
postman+json+springmvc测试批量添加实例
2018/03/31 Javascript
Nodejs模块的调用操作实例分析
2018/12/25 NodeJs
解决JQuery的ajax函数执行失败alert函数弹框一闪而过问题
2019/04/10 jQuery
Promise扫盲贴
2019/06/24 Javascript
vue通过video.js解决m3u8视频播放格式的方法
2019/07/30 Javascript
vue自定义标签和单页面多路由的实现代码
2020/05/03 Javascript
[03:14]DOTA2斧王 英雄基础教程
2013/11/26 DOTA
python常见数制转换实例分析
2015/05/09 Python
python多进程实现进程间通信实例
2017/11/24 Python
python实现批量视频分帧、保存视频帧
2019/05/31 Python
Python操作Sonqube API获取检测结果并打印过程解析
2019/11/27 Python
python实现图片转字符画
2021/02/19 Python
兰芝美国网上商城:购买LANEIGE睡眠面膜等
2017/06/30 全球购物
英国电视和家用电器购物网站:rlrdistribution.co.uk
2018/11/20 全球购物
BudgetAir印度:预订航班、酒店和汽车租赁
2019/07/07 全球购物
酒店销售主管岗位职责
2014/01/04 职场文书
群众路线教育实践活动心得体会
2014/03/07 职场文书
开工典礼策划方案
2014/05/23 职场文书
文员求职信
2014/07/15 职场文书
售房协议书范本2014
2014/10/23 职场文书
浅谈移动端中的视口(viewport)的具体使用
2021/04/13 HTML / CSS
解决vue $http的get和post请求跨域问题
2021/06/07 Vue.js