【Python】多进程-进程间通信之管道

506 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

在 《【Python】多进程-进程间通信之队列》 中,讲解了如何通过队列实现各进程之间的通信,本文将继续介绍如何使用管道进行多线程间的通信。

管道

管道与队列显示,对于单向的管道,在管道的一端放入元素,在另一端取出元素。而对于双向的管道,则可以在管道的两端存取元素。

创建一个管道

使用 multiprocessing.Pipe(duplex= True) 可一创建一个管道,这个管道默认是一个双向管道(duplex= True),其返回两个 multiprocessing.connection.Connection 对象,分别表示这个管道的两端。

使用管道

有了管道之后,就可以通过管道发送和接受对象了。

发送对象有两种方法: send(obj)send_bytes(buf, offset=0, size=None)。第一个方法发送一个对象,第二个方法则从一个 bytes-like object 对象中取出 size 大小的数据,并作为一条完整消息发送。需要注意的是,如果对象或buffer过大(接近32MB,取决于操作系统),有可能导致 ValueError 的异常。

对应的接收方法有三个: recv()recv_bytes(maxlength=None)recv_bytes_into(buffer, offset=0)。如果管道为空,则调用接收方法时会被阻塞。如果管道的另一端被关闭了,则会抛出 EOFError 异常。

除了发送和接收,还有管道的主要方法还有:

  • close() :关闭连接
  • poll(timeout=0.0):返回管道中是否有可以读取的数据,如果没有可读数据,timeout 指定了最多等待(阻塞)多长时间

代码

最后,通过代码演示一下多进程通过管道进行通信。

代码实现了一个经典的生产者-消费者问题:一个厨师生产蛋糕,三个顾客嗷嗷待哺:

import multiprocessing

import multiprocessing
import time 
import random

class cake:
    def __init__(self, time):
        self.productionTime = time 
    
    def eat(self):
        print("Eat a cake product at ", self.productionTime)
    

def cook(conn):
    for _ in range(6):
        conn.send(cake(time.time()))
        time.sleep(random.random())
    conn.close()

def customer(conn):
    for _ in range(2):
        cake = conn.recv()
        cake.eat()
        time.sleep(random.random() * 5)

if __name__ == "__main__":

    conn1, conn2 = multiprocessing.Pipe()

    p1 = multiprocessing.Process(target= cook, args=(conn1, ))
    p2 = multiprocessing.Process(target= customer, args=(conn2, ))
    p3 = multiprocessing.Process(target= customer, args=(conn2, ))
    p4 = multiprocessing.Process(target= customer, args=(conn2, ))

    p1.start()
    p2.start()
    p3.start()
    p4.start()