一、创建管道的类
1、管道的基本参数
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
参数介绍: dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
2、主要方法
<1> conn1.recv() - 接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
<2> conn1.send(obj) - 通过连接发送对象。obj是与序列化兼容的任意对象
<3> conn1.close() - 关闭连接。如果conn1被垃圾回收,将自动调用此方法
<4> conn1.fileno() - 返回连接使用的整数文件描述符
<5> conn1.poll([timeout]) - 如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
<6> conn1.recv_bytes([maxlength])
接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已 经关闭,再也不存在任何数据,将引发EOFError异常。
<7> conn.send_bytes(buffer [, offset [, size]])
通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后 调用c.recv_bytes()函数进行接 收
<8> conn1.recv_bytes_into(buffer [, offset])
接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度 大于可用的缓冲区空间,将引发BufferTooShort异常。
1 from multiprocessing import Process, Pipe
2
3 def f(conn):
4 conn.send("Hello The_Third_Wave")
5 conn.close()
6
7 if __name__ == '__main__':
8 parent_conn, child_conn = Pipe()
9 p = Process(target=f, args=(child_conn,))
10 p.start()
11 print(parent_conn.recv())
12 p.join()
应该特别注意管道端点的正确管理问题。如果是生产者或消费者中都没有使用管道的某个端点,就应将它关闭。这也说明了为何在生产者中关闭了管道的输出端,在消费者中关闭管道的输入端。如果忘记执行这些步骤,程序可能在消费者中的recv()操作上挂起(就是阻塞)。管道是由操作系统进行引用计数的,必须在所有进程中关闭管道的相同一端就会能生成EOFError异常。因此,在生产者中关闭管道不会有任何效果,除非消费者也关闭了相同的管道端点。
1 # 管道错误模拟
2 def func1(conn1,conn2):
3 try:
4 msg = conn2.recv()
5 print('>>>',msg)
6
7 #如果管道一端关闭了,那么另外一端在接收消息的时候会报错
8 msg2 = conn2.recv() #EOFError
9
10 except EOFError:
11 print('对方管道一端已经关闭')
12 conn2.close()
13
14 if __name__ == '__main__':
15 conn1, conn2 = Pipe()
16 p = Process(target=func1,args=(conn1,conn2,))
17 p.start()
18 conn1.send('小鬼!')
19 conn1.close()
20 # conn1.recv() #OSError: handle is closed 如果管道已经关闭,再接收子进程消息报错
二、总结
1、主进程将管道的两端都传送给子进程,子进程和主进程共用管道的两种报错情况,都是在recv接收的时候报错的:
(1).主进程和子进程中的管道的相同一端都关闭了,出现EOFError;
(2).如果你管道的一端在主进程和子进程中都关闭了,但是你还用这个关闭的一端去接收消息,那么就会出现OSError;
2、解决方案
所以你关闭管道的时候,就容易出现问题,需要将所有只用这个管道的进程中的两端全部关闭才行。当然也可以通过异常捕获(try:except EOFerror)来处理。
虽然我们在主进程和子进程中都打印了一下conn1一端的对象,发现两个不再同一个地址,但是子进程中的管道和主进程中的管道还是可以通信的,因为管道是同一套,系统能够记录。
我们的目的就是关闭所有的管道,那么主进程和子进程进行通信的时候,可以给子进程传管道的一端就够了,并且用我们之前学到的,信息发送完之后,再发送一个结束信号None,那么你收到的消息为None的 时候直接结束接收或者说结束循环,就不用每次都关闭各个进程中的管道了。
队列是有管道+锁实现的!进程间通信(IPC)方式二:管道(不推荐使用,了解即可),会导致数据不安全的情况出现,后面我们会说到为什么会带来数据 不安全的问题。
待续......