一、概述

Pipe返回2个连接对象(conn1, conn2),代表管道的两端,默认是双向通信的,即conn1和conn2都可以收发消息。

Pipe是数据不安全的,所以如果是多个进程之间同时收发消息时,需要自己加锁以达到数据安全。

二、常用方法与属性

Pipe([duplex])

在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道

dumplex:默认管道是双向的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。

conn1.recv()

接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。

如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。

conn1.send(obj)

通过连接发送对象。obj是与序列化兼容的任意对象

conn1.close()

关闭连接。如果conn1被垃圾回收,将自动调用此方法

conn1.fileno()

返回连接使用的整数文件描述符

conn1.poll([timeout])

如果连接上的数据可用,返回True。

timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。

conn1.recv_bytes([maxlength])

接收c.send_bytes()方法发送的一条完整的字节消息。

maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。

如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。

conn.send_bytes(buffer [, offset [, size]])

通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。

结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收

conn1.recv_bytes_into(buffer [, offset])

接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。

offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

三、示例

Pipe实现生产者消费者模型

from multiprocessing import Process,Pipe
def consumer(p,name):
produce, consume=p
produce.close()
while True:
try:
baozi=consume.recv()
print('%s 收到包子:%s' %(name,baozi))
except EOFError:
break
def producer(seq,p):
produce, consume=p
consume.close()
for i in seq:
produce.send(i)
if __name__ == '__main__':
produce,consume=Pipe()
c1=Process(target=consumer,args=((produce,consume),'c1'))
c1.start()
seq=(i for i in range(10))
producer(seq,(produce,consume))
produce.close()
consume.close()
c1.join()
print('主进程')

说明

如果是生产者或消费者中都没有使用管道的某个端点,就应将它关闭。这也说明了为何在生产者中关闭了管道的输出端,在消费者中关闭管道的输入端。如果忘记执行这些步骤,程序可能在消费者中的recv()操作上挂起。

管道是由操作系统进行引用计数的,必须在所有进程中关闭管道后才能生成EOFError异常。因此,在生产者中关闭管道不会有任何效果,除非消费者也关闭了相同的管道端点

改良版生产者消费者模型

from multiprocessing import Process,Pipe,Lock
def consumer(p,name,lock):
produce, consume=p
produce.close()
while True:
lock.acquire()
baozi=consume.recv()
lock.release()
if baozi:
print('%s 收到包子:%s' %(name,baozi))
else:
consume.close()
break
def producer(p,n):
produce, consume=p
consume.close()
for i in range(n):
produce.send(i)
produce.send(None)
produce.send(None)
produce.close()
if __name__ == '__main__':
produce,consume=Pipe()
lock = Lock()
c1=Process(target=consumer,args=((produce,consume),'c1',lock))
c2=Process(target=consumer,args=((produce,consume),'c2',lock))
p1=Process(target=producer,args=((produce,consume),10))
c1.start()
c2.start()
p1.start()
produce.close()
consume.close()
c1.join()
c2.join()
p1.join()
print('主进程')

注意

管道是数据不安全的,多个进程同时收发数据可道引起数据异常,这时候就应该配合锁使用。