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 12 Python
python3抓取中文网页的方法
Jul 28 Python
Phantomjs抓取渲染JS后的网页(Python代码)
May 13 Python
解决python读取几千万行的大表内存问题
Jun 26 Python
Python判断中文字符串是否相等的实例
Jul 06 Python
关于python列表增加元素的三种操作方法
Aug 22 Python
详解Python使用Plotly绘图工具,绘制甘特图
Apr 02 Python
PyQt5实现让QScrollArea支持鼠标拖动的操作方法
Jun 19 Python
python设置环境变量的原因和方法
Jun 24 Python
python 爬取疫情数据的源码
Feb 09 Python
Python sql注入 过滤字符串的非法字符实例
Apr 03 Python
Python实现扫码工具的示例代码
Oct 09 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
基于mysql的bbs设计(三)
2006/10/09 PHP
php对数组内元素进行随机调换的方法
2015/05/12 PHP
PHP数组中头部和尾部添加元素的方法(array_unshift,array_push)
2017/04/10 PHP
Script的加载方法小结
2011/01/12 Javascript
jQuery使用技巧简单汇总
2013/04/18 Javascript
深入了解JavaScript中的Symbol的使用方法
2015/07/28 Javascript
如何解决easyui自定义标签 datagrid edit combobox 手动输入保存不上
2015/12/26 Javascript
WordPress中利用AJAX异步获取评论用户头像的方法
2016/01/08 Javascript
一起学写js Calender日历控件
2016/04/14 Javascript
搞定immutable.js详细说明
2016/05/02 Javascript
谈谈JavaScript数组常用方法总结
2017/01/24 Javascript
通俗解释JavaScript正则表达式快速记忆
2017/08/23 Javascript
详解如何使用微信小程序云函数发送短信验证码
2019/03/13 Javascript
jquery中为什么能用$操作
2019/06/18 jQuery
Vue动态面包屑功能的实现方法
2019/07/01 Javascript
js贪心算法 钱币找零问题代码实例
2019/09/11 Javascript
如何使用vue slot创建一个模态框的实例代码
2020/05/24 Javascript
ES2020系列之空值合并运算符 '??'
2020/07/22 Javascript
vue设置默认首页的操作
2020/08/12 Javascript
Django在Win7下的安装及创建项目hello word简明教程
2014/07/14 Python
初学Python实用技巧两则
2014/08/29 Python
Python复制文件操作实例详解
2015/11/10 Python
python中urllib.unquote乱码的原因与解决方法
2017/04/24 Python
python 垃圾收集机制的实例详解
2017/08/20 Python
python一行sql太长折成多行并且有多个参数的方法
2018/07/19 Python
Flask框架模板渲染操作简单示例
2019/07/31 Python
python数组循环处理方法
2019/08/26 Python
Python爬虫爬取杭州24时温度并展示操作示例
2020/03/27 Python
利用django创建一个简易的博客网站的示例
2020/09/29 Python
html5中JavaScript removeChild 删除所有节点
2014/05/16 HTML / CSS
总经理办公室主任岗位职责
2013/11/12 职场文书
秋季运动会表扬稿
2014/01/16 职场文书
乡镇信息公开实施方案
2014/03/23 职场文书
金融专业毕业生自荐信
2014/06/26 职场文书
2015年机关纠风工作总结
2015/05/15 职场文书
导游词之无锡梅园
2019/11/28 职场文书