mutliprocessing包提供了Pipe和Queue模块来实现进程间的通信,Pipe对应的是管道,Queue对应的是队列,这两种方式都是使用消息传递来进行通信。
小知识理解:
消息管理:计算机中的消息传递是一种通信的形式,例如在进程间的通信中,进程通过发送及接收消息的形式来实现同步。
管道:管道是计算机系统进行进程通信最原始的方式,管道又分为匿名管道和命名管道,匿名管道只能用于父子进程或者兄弟进程之间的通信,命名管道可以用于无亲情关系的进程。
队列:Python中的Queue是一种先进先出队列结构,在这种队列结构中,消息的生产者将消息存入队列中,消息的消费者从队列中取出消息。
Pipe模块是对进城管道通信机制的封装,在这种通信机制中,数据只能向一个方向流动。假设有A进程和B进程,进程A只能通过管道传输给进程B,或只能接收进程B发送过来的消息。进程间需要双方通信时,需要建立两个通道。
管道的实质是一个文件系统。数据只能向一个方向流动,意味着只能对管道进行读或写。使用管道来进行通信,可以理解为一个进程将数据写入管道,另一个进程从管道中读取数据,这样就实现了通信。
Pipe模块是multiprocessing包提供的一个方法,可以返回两个连接对象。语法为multiprocessing.Pipe([duplex]),返回值为元组类型,存储了两个Connection对象(conn1,conn2),分别表示管道的两端。
duplex的值默认为True,表示该管道是双向的,这里的双向是指返回的Connection对象既可以接收也可以发送消息。如果duplex被设置为False,那么该管道是单向的,即conn1只能用于接收消息,conn2只能用于发送消息。
Connection对象的操作方法如下:
(1)Connection.send(value)方法用来发送消息,value可以是任意数据类型。
(2)Connection.recv(value)方法用来从管道接收消息。
(3)Connection.close()方法关闭管道的读或写。
下面是一个使用管道进行进程通信的实例代码,使用Pipe让一个子进程从另一个子进程中收取随机数。
# 导入
from multiprocessing import Process,Pipe
import time
import random
import os
# 定义生产者函数,届时将生产者任务指派给子进程来进行处理
def producer(conn1):
'''
:param conn1 : Connection对象,执行send()方法给消费者进程
:return :void ,无返回值
'''
# 执行os模块中的getid()方法来获取生产者进程的ID
current_pid = os.getpid()
print(f'生产者子进程的PID:{current_pid}')
# 循环5次,发送5个随机数给到消费者进程
for _ in range(5):
conn1.send({"process_id":current_pid,"number":random.randint(0,1000)})
time.sleep(0.5)
# 发送一个None值给消费者进程,表示已停止发送
conn1.send(None)
# 在子进程中关闭Connection对象
conn1.close()
# 定义消费者函数,届时将消费任务指派给子进程来进行处理
def consumer(conn2):
'''
:param conn2: Connection对象,执行recv()方法来接收从生产者进程发送的消息
:return:void ,无返回值
'''
current_pid = os.getpid()
print(f'消费者子进程的PID:{current_pid}')
while True:
# 执行recv()方法来接收从生产者进程发送的消息
message = conn2.recv()
time.sleep(0.5)
if message:
print('子进程{}收到了来自子进程{}的随机数:{}'.format(current_pid,message["process_id"],message["number"]))
else:
break # 接收到了None消息,退出循环。
# 在子进程中关闭Connection对象
conn2.close()
if __name__ == '__main__':
'''
执行Pipe方法来返回一对Connection对象
此时的Connection对象是在父进程中创建的
Pipe模块的Pipe()方法返回一个元组,存储两个Connection对象,默认两个对象可以接收也可以发送消息
'''
# 元组的解包,分别赋给两个变量
conn1,conn2 = Pipe()
# 创建生产者子进程,同时将conn1对象传递给生产者子进程
producer_process = Process(target=producer,args=(conn1,))
# 创建消费者子进程,同时将conn2对象传递给消费者子进程
consumer_process = Process(target=consumer,args=(conn2,))
'''
分别启动生产者和消费者子进程
执行Process对象的start()方法后,Process对象会在内部执行run()方法
在run()方法中会运行在target中指定的函数
'''
producer_process.start()
consumer_process.start()
'''
在父进程中关闭这一对Connection对象
管道的实质是文件,子进程引用了从父进程中传递的文件对象
所以需要同时在父子进程之间进行关闭
'''
# 分别等待生产者进程和消费者进程结束
producer_process.join()
consumer_process.join()
# 关闭Connection对象
conn1.close()
conn2.close()
运行如下图: